Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     addxtypeChild : function (tree, cntr, is_body)
234     {
235         Roo.debug && Roo.log('addxtypeChild:' + cntr);
236         var cn = this;
237         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
238         
239         
240         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
241                     (typeof(tree['flexy:foreach']) != 'undefined');
242           
243         
244         
245          skip_children = false;
246         // render the element if it's not BODY.
247         if (!is_body) {
248            
249             cn = Roo.factory(tree);
250            
251             cn.parentType = this.xtype; //??
252             cn.parentId = this.id;
253             
254             var build_from_html =  Roo.XComponent.build_from_html;
255             
256             
257             // does the container contain child eleemnts with 'xtype' attributes.
258             // that match this xtype..
259             // note - when we render we create these as well..
260             // so we should check to see if body has xtype set.
261             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
262                
263                 var self_cntr_el = Roo.get(this[cntr](false));
264                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
265                 if (echild) { 
266                     //Roo.log(Roo.XComponent.build_from_html);
267                     //Roo.log("got echild:");
268                     //Roo.log(echild);
269                 }
270                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
271                 // and are not displayed -this causes this to use up the wrong element when matching.
272                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
273                 
274                 
275                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
276                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
277                   
278                   
279                   
280                     cn.el = echild;
281                   //  Roo.log("GOT");
282                     //echild.dom.removeAttribute('xtype');
283                 } else {
284                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
285                     Roo.debug && Roo.log(self_cntr_el);
286                     Roo.debug && Roo.log(echild);
287                     Roo.debug && Roo.log(cn);
288                 }
289             }
290            
291             
292            
293             // if object has flexy:if - then it may or may not be rendered.
294             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
295                 // skip a flexy if element.
296                 Roo.debug && Roo.log('skipping render');
297                 Roo.debug && Roo.log(tree);
298                 if (!cn.el) {
299                     Roo.debug && Roo.log('skipping all children');
300                     skip_children = true;
301                 }
302                 
303              } else {
304                  
305                 // actually if flexy:foreach is found, we really want to create 
306                 // multiple copies here...
307                 //Roo.log('render');
308                 //Roo.log(this[cntr]());
309                 // some elements do not have render methods.. like the layouts...
310                 cn.render && cn.render(this[cntr](true));
311              }
312             // then add the element..
313         }
314         
315         
316         // handle the kids..
317         
318         var nitems = [];
319         /*
320         if (typeof (tree.menu) != 'undefined') {
321             tree.menu.parentType = cn.xtype;
322             tree.menu.triggerEl = cn.el;
323             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
324             
325         }
326         */
327         if (!tree.items || !tree.items.length) {
328             cn.items = nitems;
329             //Roo.log(["no children", this]);
330             
331             return cn;
332         }
333          
334         var items = tree.items;
335         delete tree.items;
336         
337         //Roo.log(items.length);
338             // add the items..
339         if (!skip_children) {    
340             for(var i =0;i < items.length;i++) {
341               //  Roo.log(['add child', items[i]]);
342                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
343             }
344         }
345         
346         cn.items = nitems;
347         
348         //Roo.log("fire childrenrendered");
349         
350         cn.fireEvent('childrenrendered', this);
351         
352         return cn;
353     },
354     /**
355      * Show a component - removes 'hidden' class
356      */
357     show : function()
358     {
359         if (this.el) {
360             this.el.removeClass('hidden');
361         }
362     },
363     /**
364      * Hide a component - adds 'hidden' class
365      */
366     hide: function()
367     {
368         if (this.el && !this.el.hasClass('hidden')) {
369             this.el.addClass('hidden');
370         }
371         
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  * 
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  * 
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393     Roo.bootstrap.Body.superclass.constructor.call(this, config);
394     this.el = Roo.get(document.body);
395     if (this.cls && this.cls.length) {
396         Roo.get(document.body).addClass(this.cls);
397     }
398 };
399
400 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
401     
402     is_body : true,// just to make sure it's constructed?
403     
404         autoCreate : {
405         cls: 'container'
406     },
407     onRender : function(ct, position)
408     {
409        /* Roo.log("Roo.bootstrap.Body - onRender");
410         if (this.cls && this.cls.length) {
411             Roo.get(document.body).addClass(this.cls);
412         }
413         // style??? xttr???
414         */
415     }
416     
417     
418  
419    
420 });
421
422  /*
423  * - LGPL
424  *
425  * button group
426  * 
427  */
428
429
430 /**
431  * @class Roo.bootstrap.ButtonGroup
432  * @extends Roo.bootstrap.Component
433  * Bootstrap ButtonGroup class
434  * @cfg {String} size lg | sm | xs (default empty normal)
435  * @cfg {String} align vertical | justified  (default none)
436  * @cfg {String} direction up | down (default down)
437  * @cfg {Boolean} toolbar false | true
438  * @cfg {Boolean} btn true | false
439  * 
440  * 
441  * @constructor
442  * Create a new Input
443  * @param {Object} config The config object
444  */
445
446 Roo.bootstrap.ButtonGroup = function(config){
447     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
448 };
449
450 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
451     
452     size: '',
453     align: '',
454     direction: '',
455     toolbar: false,
456     btn: true,
457
458     getAutoCreate : function(){
459         var cfg = {
460             cls: 'btn-group',
461             html : null
462         };
463         
464         cfg.html = this.html || cfg.html;
465         
466         if (this.toolbar) {
467             cfg = {
468                 cls: 'btn-toolbar',
469                 html: null
470             };
471             
472             return cfg;
473         }
474         
475         if (['vertical','justified'].indexOf(this.align)!==-1) {
476             cfg.cls = 'btn-group-' + this.align;
477             
478             if (this.align == 'justified') {
479                 console.log(this.items);
480             }
481         }
482         
483         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
484             cfg.cls += ' btn-group-' + this.size;
485         }
486         
487         if (this.direction == 'up') {
488             cfg.cls += ' dropup' ;
489         }
490         
491         return cfg;
492     }
493    
494 });
495
496  /*
497  * - LGPL
498  *
499  * button
500  * 
501  */
502
503 /**
504  * @class Roo.bootstrap.Button
505  * @extends Roo.bootstrap.Component
506  * Bootstrap Button class
507  * @cfg {String} html The button content
508  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
509  * @cfg {String} size ( lg | sm | xs)
510  * @cfg {String} tag ( a | input | submit)
511  * @cfg {String} href empty or href
512  * @cfg {Boolean} disabled default false;
513  * @cfg {Boolean} isClose default false;
514  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
515  * @cfg {String} badge text for badge
516  * @cfg {String} theme default 
517  * @cfg {Boolean} inverse 
518  * @cfg {Boolean} toggle 
519  * @cfg {String} ontext text for on toggle state
520  * @cfg {String} offtext text for off toggle state
521  * @cfg {Boolean} defaulton 
522  * @cfg {Boolean} preventDefault  default true
523  * @cfg {Boolean} removeClass remove the standard class..
524  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
525  * 
526  * @constructor
527  * Create a new button
528  * @param {Object} config The config object
529  */
530
531
532 Roo.bootstrap.Button = function(config){
533     Roo.bootstrap.Button.superclass.constructor.call(this, config);
534     this.addEvents({
535         // raw events
536         /**
537          * @event click
538          * When a butotn is pressed
539          * @param {Roo.bootstrap.Button} this
540          * @param {Roo.EventObject} e
541          */
542         "click" : true,
543          /**
544          * @event toggle
545          * After the button has been toggles
546          * @param {Roo.EventObject} e
547          * @param {boolean} pressed (also available as button.pressed)
548          */
549         "toggle" : true
550     });
551 };
552
553 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
554     html: false,
555     active: false,
556     weight: '',
557     size: '',
558     tag: 'button',
559     href: '',
560     disabled: false,
561     isClose: false,
562     glyphicon: '',
563     badge: '',
564     theme: 'default',
565     inverse: false,
566     
567     toggle: false,
568     ontext: 'ON',
569     offtext: 'OFF',
570     defaulton: true,
571     preventDefault: true,
572     removeClass: false,
573     name: false,
574     target: false,
575     
576     
577     pressed : null,
578      
579     
580     getAutoCreate : function(){
581         
582         var cfg = {
583             tag : 'button',
584             cls : 'roo-button',
585             html: ''
586         };
587         
588         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
589             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
590             this.tag = 'button';
591         } else {
592             cfg.tag = this.tag;
593         }
594         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
595         
596         if (this.toggle == true) {
597             cfg={
598                 tag: 'div',
599                 cls: 'slider-frame roo-button',
600                 cn: [
601                     {
602                         tag: 'span',
603                         'data-on-text':'ON',
604                         'data-off-text':'OFF',
605                         cls: 'slider-button',
606                         html: this.offtext
607                     }
608                 ]
609             };
610             
611             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
612                 cfg.cls += ' '+this.weight;
613             }
614             
615             return cfg;
616         }
617         
618         if (this.isClose) {
619             cfg.cls += ' close';
620             
621             cfg["aria-hidden"] = true;
622             
623             cfg.html = "&times;";
624             
625             return cfg;
626         }
627         
628          
629         if (this.theme==='default') {
630             cfg.cls = 'btn roo-button';
631             
632             //if (this.parentType != 'Navbar') {
633             this.weight = this.weight.length ?  this.weight : 'default';
634             //}
635             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
636                 
637                 cfg.cls += ' btn-' + this.weight;
638             }
639         } else if (this.theme==='glow') {
640             
641             cfg.tag = 'a';
642             cfg.cls = 'btn-glow roo-button';
643             
644             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
645                 
646                 cfg.cls += ' ' + this.weight;
647             }
648         }
649    
650         
651         if (this.inverse) {
652             this.cls += ' inverse';
653         }
654         
655         
656         if (this.active) {
657             cfg.cls += ' active';
658         }
659         
660         if (this.disabled) {
661             cfg.disabled = 'disabled';
662         }
663         
664         if (this.items) {
665             Roo.log('changing to ul' );
666             cfg.tag = 'ul';
667             this.glyphicon = 'caret';
668         }
669         
670         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
671          
672         //gsRoo.log(this.parentType);
673         if (this.parentType === 'Navbar' && !this.parent().bar) {
674             Roo.log('changing to li?');
675             
676             cfg.tag = 'li';
677             
678             cfg.cls = '';
679             cfg.cn =  [{
680                 tag : 'a',
681                 cls : 'roo-button',
682                 html : this.html,
683                 href : this.href || '#'
684             }];
685             if (this.menu) {
686                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
687                 cfg.cls += ' dropdown';
688             }   
689             
690             delete cfg.html;
691             
692         }
693         
694        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
695         
696         if (this.glyphicon) {
697             cfg.html = ' ' + cfg.html;
698             
699             cfg.cn = [
700                 {
701                     tag: 'span',
702                     cls: 'glyphicon glyphicon-' + this.glyphicon
703                 }
704             ];
705         }
706         
707         if (this.badge) {
708             cfg.html += ' ';
709             
710             cfg.tag = 'a';
711             
712 //            cfg.cls='btn roo-button';
713             
714             cfg.href=this.href;
715             
716             var value = cfg.html;
717             
718             if(this.glyphicon){
719                 value = {
720                             tag: 'span',
721                             cls: 'glyphicon glyphicon-' + this.glyphicon,
722                             html: this.html
723                         };
724                 
725             }
726             
727             cfg.cn = [
728                 value,
729                 {
730                     tag: 'span',
731                     cls: 'badge',
732                     html: this.badge
733                 }
734             ];
735             
736             cfg.html='';
737         }
738         
739         if (this.menu) {
740             cfg.cls += ' dropdown';
741             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
742         }
743         
744         if (cfg.tag !== 'a' && this.href !== '') {
745             throw "Tag must be a to set href.";
746         } else if (this.href.length > 0) {
747             cfg.href = this.href;
748         }
749         
750         if(this.removeClass){
751             cfg.cls = '';
752         }
753         
754         if(this.target){
755             cfg.target = this.target;
756         }
757         
758         return cfg;
759     },
760     initEvents: function() {
761        // Roo.log('init events?');
762 //        Roo.log(this.el.dom);
763         // add the menu...
764         
765         if (typeof (this.menu) != 'undefined') {
766             this.menu.parentType = this.xtype;
767             this.menu.triggerEl = this.el;
768             this.addxtype(Roo.apply({}, this.menu));
769         }
770
771
772        if (this.el.hasClass('roo-button')) {
773             this.el.on('click', this.onClick, this);
774        } else {
775             this.el.select('.roo-button').on('click', this.onClick, this);
776        }
777        
778        if(this.removeClass){
779            this.el.on('click', this.onClick, this);
780        }
781        
782        this.el.enableDisplayMode();
783         
784     },
785     onClick : function(e)
786     {
787         if (this.disabled) {
788             return;
789         }
790         
791         
792         Roo.log('button on click ');
793         if(this.preventDefault){
794             e.preventDefault();
795         }
796         if (this.pressed === true || this.pressed === false) {
797             this.pressed = !this.pressed;
798             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
799             this.fireEvent('toggle', this, e, this.pressed);
800         }
801         
802         
803         this.fireEvent('click', this, e);
804     },
805     
806     /**
807      * Enables this button
808      */
809     enable : function()
810     {
811         this.disabled = false;
812         this.el.removeClass('disabled');
813     },
814     
815     /**
816      * Disable this button
817      */
818     disable : function()
819     {
820         this.disabled = true;
821         this.el.addClass('disabled');
822     },
823      /**
824      * sets the active state on/off, 
825      * @param {Boolean} state (optional) Force a particular state
826      */
827     setActive : function(v) {
828         
829         this.el[v ? 'addClass' : 'removeClass']('active');
830     },
831      /**
832      * toggles the current active state 
833      */
834     toggleActive : function()
835     {
836        var active = this.el.hasClass('active');
837        this.setActive(!active);
838        
839         
840     },
841     setText : function(str)
842     {
843         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
844     },
845     getText : function()
846     {
847         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
848     },
849     hide: function() {
850        
851      
852         this.el.hide();   
853     },
854     show: function() {
855        
856         this.el.show();   
857     }
858     
859     
860 });
861
862  /*
863  * - LGPL
864  *
865  * column
866  * 
867  */
868
869 /**
870  * @class Roo.bootstrap.Column
871  * @extends Roo.bootstrap.Component
872  * Bootstrap Column class
873  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
874  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
875  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
876  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
877  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
878  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
879  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
880  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
881  *
882  * 
883  * @cfg {Boolean} hidden (true|false) hide the element
884  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
885  * @cfg {String} fa (ban|check|...) font awesome icon
886  * @cfg {Number} fasize (1|2|....) font awsome size
887
888  * @cfg {String} icon (info-sign|check|...) glyphicon name
889
890  * @cfg {String} html content of column.
891  * 
892  * @constructor
893  * Create a new Column
894  * @param {Object} config The config object
895  */
896
897 Roo.bootstrap.Column = function(config){
898     Roo.bootstrap.Column.superclass.constructor.call(this, config);
899 };
900
901 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
902     
903     xs: false,
904     sm: false,
905     md: false,
906     lg: false,
907     xsoff: false,
908     smoff: false,
909     mdoff: false,
910     lgoff: false,
911     html: '',
912     offset: 0,
913     alert: false,
914     fa: false,
915     icon : false,
916     hidden : false,
917     fasize : 1,
918     
919     getAutoCreate : function(){
920         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
921         
922         cfg = {
923             tag: 'div',
924             cls: 'column'
925         };
926         
927         var settings=this;
928         ['xs','sm','md','lg'].map(function(size){
929             //Roo.log( size + ':' + settings[size]);
930             
931             if (settings[size+'off'] !== false) {
932                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
933             }
934             
935             if (settings[size] === false) {
936                 return;
937             }
938             
939             if (!settings[size]) { // 0 = hidden
940                 cfg.cls += ' hidden-' + size;
941                 return;
942             }
943             cfg.cls += ' col-' + size + '-' + settings[size];
944             
945         });
946         
947         if (this.hidden) {
948             cfg.cls += ' hidden';
949         }
950         
951         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
952             cfg.cls +=' alert alert-' + this.alert;
953         }
954         
955         
956         if (this.html.length) {
957             cfg.html = this.html;
958         }
959         if (this.fa) {
960             var fasize = '';
961             if (this.fasize > 1) {
962                 fasize = ' fa-' + this.fasize + 'x';
963             }
964             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
965             
966             
967         }
968         if (this.icon) {
969             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
970         }
971         
972         return cfg;
973     }
974    
975 });
976
977  
978
979  /*
980  * - LGPL
981  *
982  * page container.
983  * 
984  */
985
986
987 /**
988  * @class Roo.bootstrap.Container
989  * @extends Roo.bootstrap.Component
990  * Bootstrap Container class
991  * @cfg {Boolean} jumbotron is it a jumbotron element
992  * @cfg {String} html content of element
993  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
994  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
995  * @cfg {String} header content of header (for panel)
996  * @cfg {String} footer content of footer (for panel)
997  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
998  * @cfg {String} tag (header|aside|section) type of HTML tag.
999  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1000  * @cfg {String} fa font awesome icon
1001  * @cfg {String} icon (info-sign|check|...) glyphicon name
1002  * @cfg {Boolean} hidden (true|false) hide the element
1003  * @cfg {Boolean} expandable (true|false) default false
1004  * @cfg {Boolean} expanded (true|false) default true
1005  * @cfg {String} rheader contet on the right of header
1006  * @cfg {Boolean} clickable (true|false) default false
1007
1008  *     
1009  * @constructor
1010  * Create a new Container
1011  * @param {Object} config The config object
1012  */
1013
1014 Roo.bootstrap.Container = function(config){
1015     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019          /**
1020          * @event expand
1021          * After the panel has been expand
1022          * 
1023          * @param {Roo.bootstrap.Container} this
1024          */
1025         "expand" : true,
1026         /**
1027          * @event collapse
1028          * After the panel has been collapsed
1029          * 
1030          * @param {Roo.bootstrap.Container} this
1031          */
1032         "collapse" : true,
1033         /**
1034          * @event click
1035          * When a element is chick
1036          * @param {Roo.bootstrap.Container} this
1037          * @param {Roo.EventObject} e
1038          */
1039         "click" : true
1040     });
1041 };
1042
1043 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1044     
1045     jumbotron : false,
1046     well: '',
1047     panel : '',
1048     header: '',
1049     footer : '',
1050     sticky: '',
1051     tag : false,
1052     alert : false,
1053     fa: false,
1054     icon : false,
1055     expandable : false,
1056     rheader : '',
1057     expanded : true,
1058     clickable: false,
1059   
1060      
1061     getChildContainer : function() {
1062         
1063         if(!this.el){
1064             return false;
1065         }
1066         
1067         if (this.panel.length) {
1068             return this.el.select('.panel-body',true).first();
1069         }
1070         
1071         return this.el;
1072     },
1073     
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : this.tag || 'div',
1079             html : '',
1080             cls : ''
1081         };
1082         if (this.jumbotron) {
1083             cfg.cls = 'jumbotron';
1084         }
1085         
1086         
1087         
1088         // - this is applied by the parent..
1089         //if (this.cls) {
1090         //    cfg.cls = this.cls + '';
1091         //}
1092         
1093         if (this.sticky.length) {
1094             
1095             var bd = Roo.get(document.body);
1096             if (!bd.hasClass('bootstrap-sticky')) {
1097                 bd.addClass('bootstrap-sticky');
1098                 Roo.select('html',true).setStyle('height', '100%');
1099             }
1100              
1101             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1102         }
1103         
1104         
1105         if (this.well.length) {
1106             switch (this.well) {
1107                 case 'lg':
1108                 case 'sm':
1109                     cfg.cls +=' well well-' +this.well;
1110                     break;
1111                 default:
1112                     cfg.cls +=' well';
1113                     break;
1114             }
1115         }
1116         
1117         if (this.hidden) {
1118             cfg.cls += ' hidden';
1119         }
1120         
1121         
1122         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1123             cfg.cls +=' alert alert-' + this.alert;
1124         }
1125         
1126         var body = cfg;
1127         
1128         if (this.panel.length) {
1129             cfg.cls += ' panel panel-' + this.panel;
1130             cfg.cn = [];
1131             if (this.header.length) {
1132                 
1133                 var h = [];
1134                 
1135                 if(this.expandable){
1136                     
1137                     cfg.cls = cfg.cls + ' expandable';
1138                     
1139                     h.push({
1140                         tag: 'i',
1141                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1142                     });
1143                     
1144                 }
1145                 
1146                 h.push(
1147                     {
1148                         tag: 'span',
1149                         cls : 'panel-title',
1150                         html : (this.expandable ? '&nbsp;' : '') + this.header
1151                     },
1152                     {
1153                         tag: 'span',
1154                         cls: 'panel-header-right',
1155                         html: this.rheader
1156                     }
1157                 );
1158                 
1159                 cfg.cn.push({
1160                     cls : 'panel-heading',
1161                     style : this.expandable ? 'cursor: pointer' : '',
1162                     cn : h
1163                 });
1164                 
1165             }
1166             
1167             body = false;
1168             cfg.cn.push({
1169                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1170                 html : this.html
1171             });
1172             
1173             
1174             if (this.footer.length) {
1175                 cfg.cn.push({
1176                     cls : 'panel-footer',
1177                     html : this.footer
1178                     
1179                 });
1180             }
1181             
1182         }
1183         
1184         if (body) {
1185             body.html = this.html || cfg.html;
1186             // prefix with the icons..
1187             if (this.fa) {
1188                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1189             }
1190             if (this.icon) {
1191                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1192             }
1193             
1194             
1195         }
1196         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1197             cfg.cls =  'container';
1198         }
1199         
1200         return cfg;
1201     },
1202     
1203     initEvents: function() 
1204     {
1205         if(this.expandable){
1206             var headerEl = this.headerEl();
1207         
1208             if(headerEl){
1209                 headerEl.on('click', this.onToggleClick, this);
1210             }
1211         }
1212         
1213         if(this.clickable){
1214             this.el.on('click', this.onClick, this);
1215         }
1216         
1217     },
1218     
1219     onToggleClick : function()
1220     {
1221         var headerEl = this.headerEl();
1222         
1223         if(!headerEl){
1224             return;
1225         }
1226         
1227         if(this.expanded){
1228             this.collapse();
1229             return;
1230         }
1231         
1232         this.expand();
1233     },
1234     
1235     expand : function()
1236     {
1237         if(this.fireEvent('expand', this)) {
1238             
1239             this.expanded = true;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1242             
1243             this.el.select('.panel-body',true).first().removeClass('hide');
1244             
1245             var toggleEl = this.toggleEl();
1246
1247             if(!toggleEl){
1248                 return;
1249             }
1250
1251             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1252         }
1253         
1254     },
1255     
1256     collapse : function()
1257     {
1258         if(this.fireEvent('collapse', this)) {
1259             
1260             this.expanded = false;
1261             
1262             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1263             this.el.select('.panel-body',true).first().addClass('hide');
1264         
1265             var toggleEl = this.toggleEl();
1266
1267             if(!toggleEl){
1268                 return;
1269             }
1270
1271             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1272         }
1273     },
1274     
1275     toggleEl : function()
1276     {
1277         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1278             return;
1279         }
1280         
1281         return this.el.select('.panel-heading .fa',true).first();
1282     },
1283     
1284     headerEl : function()
1285     {
1286         if(!this.el || !this.panel.length || !this.header.length){
1287             return;
1288         }
1289         
1290         return this.el.select('.panel-heading',true).first()
1291     },
1292     
1293     titleEl : function()
1294     {
1295         if(!this.el || !this.panel.length || !this.header.length){
1296             return;
1297         }
1298         
1299         return this.el.select('.panel-title',true).first();
1300     },
1301     
1302     setTitle : function(v)
1303     {
1304         var titleEl = this.titleEl();
1305         
1306         if(!titleEl){
1307             return;
1308         }
1309         
1310         titleEl.dom.innerHTML = v;
1311     },
1312     
1313     getTitle : function()
1314     {
1315         
1316         var titleEl = this.titleEl();
1317         
1318         if(!titleEl){
1319             return '';
1320         }
1321         
1322         return titleEl.dom.innerHTML;
1323     },
1324     
1325     setRightTitle : function(v)
1326     {
1327         var t = this.el.select('.panel-header-right',true).first();
1328         
1329         if(!t){
1330             return;
1331         }
1332         
1333         t.dom.innerHTML = v;
1334     },
1335     
1336     onClick : function(e)
1337     {
1338         e.preventDefault();
1339         
1340         this.fireEvent('click', this, e);
1341     }
1342    
1343 });
1344
1345  /*
1346  * - LGPL
1347  *
1348  * image
1349  * 
1350  */
1351
1352
1353 /**
1354  * @class Roo.bootstrap.Img
1355  * @extends Roo.bootstrap.Component
1356  * Bootstrap Img class
1357  * @cfg {Boolean} imgResponsive false | true
1358  * @cfg {String} border rounded | circle | thumbnail
1359  * @cfg {String} src image source
1360  * @cfg {String} alt image alternative text
1361  * @cfg {String} href a tag href
1362  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1363  * @cfg {String} xsUrl xs image source
1364  * @cfg {String} smUrl sm image source
1365  * @cfg {String} mdUrl md image source
1366  * @cfg {String} lgUrl lg image source
1367  * 
1368  * @constructor
1369  * Create a new Input
1370  * @param {Object} config The config object
1371  */
1372
1373 Roo.bootstrap.Img = function(config){
1374     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1375     
1376     this.addEvents({
1377         // img events
1378         /**
1379          * @event click
1380          * The img click event for the img.
1381          * @param {Roo.EventObject} e
1382          */
1383         "click" : true
1384     });
1385 };
1386
1387 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1388     
1389     imgResponsive: true,
1390     border: '',
1391     src: 'about:blank',
1392     href: false,
1393     target: false,
1394     xsUrl: '',
1395     smUrl: '',
1396     mdUrl: '',
1397     lgUrl: '',
1398
1399     getAutoCreate : function()
1400     {   
1401         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1402             return this.createSingleImg();
1403         }
1404         
1405         var cfg = {
1406             tag: 'div',
1407             cls: 'roo-image-responsive-group',
1408             cn: []
1409         };
1410         var _this = this;
1411         
1412         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1413             
1414             if(!_this[size + 'Url']){
1415                 return;
1416             }
1417             
1418             var img = {
1419                 tag: 'img',
1420                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1421                 html: _this.html || cfg.html,
1422                 src: _this[size + 'Url']
1423             };
1424             
1425             img.cls += ' roo-image-responsive-' + size;
1426             
1427             var s = ['xs', 'sm', 'md', 'lg'];
1428             
1429             s.splice(s.indexOf(size), 1);
1430             
1431             Roo.each(s, function(ss){
1432                 img.cls += ' hidden-' + ss;
1433             });
1434             
1435             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1436                 cfg.cls += ' img-' + _this.border;
1437             }
1438             
1439             if(_this.alt){
1440                 cfg.alt = _this.alt;
1441             }
1442             
1443             if(_this.href){
1444                 var a = {
1445                     tag: 'a',
1446                     href: _this.href,
1447                     cn: [
1448                         img
1449                     ]
1450                 };
1451
1452                 if(this.target){
1453                     a.target = _this.target;
1454                 }
1455             }
1456             
1457             cfg.cn.push((_this.href) ? a : img);
1458             
1459         });
1460         
1461         return cfg;
1462     },
1463     
1464     createSingleImg : function()
1465     {
1466         var cfg = {
1467             tag: 'img',
1468             cls: (this.imgResponsive) ? 'img-responsive' : '',
1469             html : null,
1470             src : 'about:blank'  // just incase src get's set to undefined?!?
1471         };
1472         
1473         cfg.html = this.html || cfg.html;
1474         
1475         cfg.src = this.src || cfg.src;
1476         
1477         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1478             cfg.cls += ' img-' + this.border;
1479         }
1480         
1481         if(this.alt){
1482             cfg.alt = this.alt;
1483         }
1484         
1485         if(this.href){
1486             var a = {
1487                 tag: 'a',
1488                 href: this.href,
1489                 cn: [
1490                     cfg
1491                 ]
1492             };
1493             
1494             if(this.target){
1495                 a.target = this.target;
1496             }
1497             
1498         }
1499         
1500         return (this.href) ? a : cfg;
1501     },
1502     
1503     initEvents: function() 
1504     {
1505         if(!this.href){
1506             this.el.on('click', this.onClick, this);
1507         }
1508         
1509     },
1510     
1511     onClick : function(e)
1512     {
1513         Roo.log('img onclick');
1514         this.fireEvent('click', this, e);
1515     }
1516    
1517 });
1518
1519  /*
1520  * - LGPL
1521  *
1522  * image
1523  * 
1524  */
1525
1526
1527 /**
1528  * @class Roo.bootstrap.Link
1529  * @extends Roo.bootstrap.Component
1530  * Bootstrap Link Class
1531  * @cfg {String} alt image alternative text
1532  * @cfg {String} href a tag href
1533  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1534  * @cfg {String} html the content of the link.
1535  * @cfg {String} anchor name for the anchor link
1536  * @cfg {String} fa - favicon
1537
1538  * @cfg {Boolean} preventDefault (true | false) default false
1539
1540  * 
1541  * @constructor
1542  * Create a new Input
1543  * @param {Object} config The config object
1544  */
1545
1546 Roo.bootstrap.Link = function(config){
1547     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1548     
1549     this.addEvents({
1550         // img events
1551         /**
1552          * @event click
1553          * The img click event for the img.
1554          * @param {Roo.EventObject} e
1555          */
1556         "click" : true
1557     });
1558 };
1559
1560 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1561     
1562     href: false,
1563     target: false,
1564     preventDefault: false,
1565     anchor : false,
1566     alt : false,
1567     fa: false,
1568
1569
1570     getAutoCreate : function()
1571     {
1572         var html = this.html || '';
1573         
1574         if (this.fa !== false) {
1575             html = '<i class="fa fa-' + this.fa + '"></i>';
1576         }
1577         var cfg = {
1578             tag: 'a'
1579         };
1580         // anchor's do not require html/href...
1581         if (this.anchor === false) {
1582             cfg.html = html;
1583             cfg.href = this.href || '#';
1584         } else {
1585             cfg.name = this.anchor;
1586             if (this.html !== false || this.fa !== false) {
1587                 cfg.html = html;
1588             }
1589             if (this.href !== false) {
1590                 cfg.href = this.href;
1591             }
1592         }
1593         
1594         if(this.alt !== false){
1595             cfg.alt = this.alt;
1596         }
1597         
1598         
1599         if(this.target !== false) {
1600             cfg.target = this.target;
1601         }
1602         
1603         return cfg;
1604     },
1605     
1606     initEvents: function() {
1607         
1608         if(!this.href || this.preventDefault){
1609             this.el.on('click', this.onClick, this);
1610         }
1611     },
1612     
1613     onClick : function(e)
1614     {
1615         if(this.preventDefault){
1616             e.preventDefault();
1617         }
1618         //Roo.log('img onclick');
1619         this.fireEvent('click', this, e);
1620     }
1621    
1622 });
1623
1624  /*
1625  * - LGPL
1626  *
1627  * header
1628  * 
1629  */
1630
1631 /**
1632  * @class Roo.bootstrap.Header
1633  * @extends Roo.bootstrap.Component
1634  * Bootstrap Header class
1635  * @cfg {String} html content of header
1636  * @cfg {Number} level (1|2|3|4|5|6) default 1
1637  * 
1638  * @constructor
1639  * Create a new Header
1640  * @param {Object} config The config object
1641  */
1642
1643
1644 Roo.bootstrap.Header  = function(config){
1645     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1649     
1650     //href : false,
1651     html : false,
1652     level : 1,
1653     
1654     
1655     
1656     getAutoCreate : function(){
1657         
1658         
1659         
1660         var cfg = {
1661             tag: 'h' + (1 *this.level),
1662             html: this.html || ''
1663         } ;
1664         
1665         return cfg;
1666     }
1667    
1668 });
1669
1670  
1671
1672  /*
1673  * Based on:
1674  * Ext JS Library 1.1.1
1675  * Copyright(c) 2006-2007, Ext JS, LLC.
1676  *
1677  * Originally Released Under LGPL - original licence link has changed is not relivant.
1678  *
1679  * Fork - LGPL
1680  * <script type="text/javascript">
1681  */
1682  
1683 /**
1684  * @class Roo.bootstrap.MenuMgr
1685  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1686  * @singleton
1687  */
1688 Roo.bootstrap.MenuMgr = function(){
1689    var menus, active, groups = {}, attached = false, lastShow = new Date();
1690
1691    // private - called when first menu is created
1692    function init(){
1693        menus = {};
1694        active = new Roo.util.MixedCollection();
1695        Roo.get(document).addKeyListener(27, function(){
1696            if(active.length > 0){
1697                hideAll();
1698            }
1699        });
1700    }
1701
1702    // private
1703    function hideAll(){
1704        if(active && active.length > 0){
1705            var c = active.clone();
1706            c.each(function(m){
1707                m.hide();
1708            });
1709        }
1710    }
1711
1712    // private
1713    function onHide(m){
1714        active.remove(m);
1715        if(active.length < 1){
1716            Roo.get(document).un("mouseup", onMouseDown);
1717             
1718            attached = false;
1719        }
1720    }
1721
1722    // private
1723    function onShow(m){
1724        var last = active.last();
1725        lastShow = new Date();
1726        active.add(m);
1727        if(!attached){
1728           Roo.get(document).on("mouseup", onMouseDown);
1729            
1730            attached = true;
1731        }
1732        if(m.parentMenu){
1733           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1734           m.parentMenu.activeChild = m;
1735        }else if(last && last.isVisible()){
1736           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1737        }
1738    }
1739
1740    // private
1741    function onBeforeHide(m){
1742        if(m.activeChild){
1743            m.activeChild.hide();
1744        }
1745        if(m.autoHideTimer){
1746            clearTimeout(m.autoHideTimer);
1747            delete m.autoHideTimer;
1748        }
1749    }
1750
1751    // private
1752    function onBeforeShow(m){
1753        var pm = m.parentMenu;
1754        if(!pm && !m.allowOtherMenus){
1755            hideAll();
1756        }else if(pm && pm.activeChild && active != m){
1757            pm.activeChild.hide();
1758        }
1759    }
1760
1761    // private this should really trigger on mouseup..
1762    function onMouseDown(e){
1763         Roo.log("on Mouse Up");
1764         
1765         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1766             Roo.log("MenuManager hideAll");
1767             hideAll();
1768             e.stopEvent();
1769         }
1770         
1771         
1772    }
1773
1774    // private
1775    function onBeforeCheck(mi, state){
1776        if(state){
1777            var g = groups[mi.group];
1778            for(var i = 0, l = g.length; i < l; i++){
1779                if(g[i] != mi){
1780                    g[i].setChecked(false);
1781                }
1782            }
1783        }
1784    }
1785
1786    return {
1787
1788        /**
1789         * Hides all menus that are currently visible
1790         */
1791        hideAll : function(){
1792             hideAll();  
1793        },
1794
1795        // private
1796        register : function(menu){
1797            if(!menus){
1798                init();
1799            }
1800            menus[menu.id] = menu;
1801            menu.on("beforehide", onBeforeHide);
1802            menu.on("hide", onHide);
1803            menu.on("beforeshow", onBeforeShow);
1804            menu.on("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                if(!groups[g]){
1808                    groups[g] = [];
1809                }
1810                groups[g].push(menu);
1811                menu.on("checkchange", onCheck);
1812            }
1813        },
1814
1815         /**
1816          * Returns a {@link Roo.menu.Menu} object
1817          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1818          * be used to generate and return a new Menu instance.
1819          */
1820        get : function(menu){
1821            if(typeof menu == "string"){ // menu id
1822                return menus[menu];
1823            }else if(menu.events){  // menu instance
1824                return menu;
1825            }
1826            /*else if(typeof menu.length == 'number'){ // array of menu items?
1827                return new Roo.bootstrap.Menu({items:menu});
1828            }else{ // otherwise, must be a config
1829                return new Roo.bootstrap.Menu(menu);
1830            }
1831            */
1832            return false;
1833        },
1834
1835        // private
1836        unregister : function(menu){
1837            delete menus[menu.id];
1838            menu.un("beforehide", onBeforeHide);
1839            menu.un("hide", onHide);
1840            menu.un("beforeshow", onBeforeShow);
1841            menu.un("show", onShow);
1842            var g = menu.group;
1843            if(g && menu.events["checkchange"]){
1844                groups[g].remove(menu);
1845                menu.un("checkchange", onCheck);
1846            }
1847        },
1848
1849        // private
1850        registerCheckable : function(menuItem){
1851            var g = menuItem.group;
1852            if(g){
1853                if(!groups[g]){
1854                    groups[g] = [];
1855                }
1856                groups[g].push(menuItem);
1857                menuItem.on("beforecheckchange", onBeforeCheck);
1858            }
1859        },
1860
1861        // private
1862        unregisterCheckable : function(menuItem){
1863            var g = menuItem.group;
1864            if(g){
1865                groups[g].remove(menuItem);
1866                menuItem.un("beforecheckchange", onBeforeCheck);
1867            }
1868        }
1869    };
1870 }();/*
1871  * - LGPL
1872  *
1873  * menu
1874  * 
1875  */
1876
1877 /**
1878  * @class Roo.bootstrap.Menu
1879  * @extends Roo.bootstrap.Component
1880  * Bootstrap Menu class - container for MenuItems
1881  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1882  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1883  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1884  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1885  * 
1886  * @constructor
1887  * Create a new Menu
1888  * @param {Object} config The config object
1889  */
1890
1891
1892 Roo.bootstrap.Menu = function(config){
1893     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1894     if (this.registerMenu && this.type != 'treeview')  {
1895         Roo.bootstrap.MenuMgr.register(this);
1896     }
1897     this.addEvents({
1898         /**
1899          * @event beforeshow
1900          * Fires before this menu is displayed
1901          * @param {Roo.menu.Menu} this
1902          */
1903         beforeshow : true,
1904         /**
1905          * @event beforehide
1906          * Fires before this menu is hidden
1907          * @param {Roo.menu.Menu} this
1908          */
1909         beforehide : true,
1910         /**
1911          * @event show
1912          * Fires after this menu is displayed
1913          * @param {Roo.menu.Menu} this
1914          */
1915         show : true,
1916         /**
1917          * @event hide
1918          * Fires after this menu is hidden
1919          * @param {Roo.menu.Menu} this
1920          */
1921         hide : true,
1922         /**
1923          * @event click
1924          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1925          * @param {Roo.menu.Menu} this
1926          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1927          * @param {Roo.EventObject} e
1928          */
1929         click : true,
1930         /**
1931          * @event mouseover
1932          * Fires when the mouse is hovering over this menu
1933          * @param {Roo.menu.Menu} this
1934          * @param {Roo.EventObject} e
1935          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1936          */
1937         mouseover : true,
1938         /**
1939          * @event mouseout
1940          * Fires when the mouse exits this menu
1941          * @param {Roo.menu.Menu} this
1942          * @param {Roo.EventObject} e
1943          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1944          */
1945         mouseout : true,
1946         /**
1947          * @event itemclick
1948          * Fires when a menu item contained in this menu is clicked
1949          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1950          * @param {Roo.EventObject} e
1951          */
1952         itemclick: true
1953     });
1954     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1955 };
1956
1957 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1958     
1959    /// html : false,
1960     //align : '',
1961     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1962     type: false,
1963     /**
1964      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1965      */
1966     registerMenu : true,
1967     
1968     menuItems :false, // stores the menu items..
1969     
1970     hidden:true,
1971         
1972     parentMenu : false,
1973     
1974     stopEvent : true,
1975     
1976     isLink : false,
1977     
1978     getChildContainer : function() {
1979         return this.el;  
1980     },
1981     
1982     getAutoCreate : function(){
1983          
1984         //if (['right'].indexOf(this.align)!==-1) {
1985         //    cfg.cn[1].cls += ' pull-right'
1986         //}
1987         
1988         
1989         var cfg = {
1990             tag : 'ul',
1991             cls : 'dropdown-menu' ,
1992             style : 'z-index:1000'
1993             
1994         };
1995         
1996         if (this.type === 'submenu') {
1997             cfg.cls = 'submenu active';
1998         }
1999         if (this.type === 'treeview') {
2000             cfg.cls = 'treeview-menu';
2001         }
2002         
2003         return cfg;
2004     },
2005     initEvents : function() {
2006         
2007        // Roo.log("ADD event");
2008        // Roo.log(this.triggerEl.dom);
2009         
2010         this.triggerEl.on('click', this.onTriggerClick, this);
2011         
2012         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2013         
2014         this.triggerEl.addClass('dropdown-toggle');
2015         
2016         if (Roo.isTouch) {
2017             this.el.on('touchstart'  , this.onTouch, this);
2018         }
2019         this.el.on('click' , this.onClick, this);
2020
2021         this.el.on("mouseover", this.onMouseOver, this);
2022         this.el.on("mouseout", this.onMouseOut, this);
2023         
2024     },
2025     
2026     findTargetItem : function(e)
2027     {
2028         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2029         if(!t){
2030             return false;
2031         }
2032         //Roo.log(t);         Roo.log(t.id);
2033         if(t && t.id){
2034             //Roo.log(this.menuitems);
2035             return this.menuitems.get(t.id);
2036             
2037             //return this.items.get(t.menuItemId);
2038         }
2039         
2040         return false;
2041     },
2042     
2043     onTouch : function(e) 
2044     {
2045         Roo.log("menu.onTouch");
2046         //e.stopEvent(); this make the user popdown broken
2047         this.onClick(e);
2048     },
2049     
2050     onClick : function(e)
2051     {
2052         Roo.log("menu.onClick");
2053         
2054         var t = this.findTargetItem(e);
2055         if(!t || t.isContainer){
2056             return;
2057         }
2058         Roo.log(e);
2059         /*
2060         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2061             if(t == this.activeItem && t.shouldDeactivate(e)){
2062                 this.activeItem.deactivate();
2063                 delete this.activeItem;
2064                 return;
2065             }
2066             if(t.canActivate){
2067                 this.setActiveItem(t, true);
2068             }
2069             return;
2070             
2071             
2072         }
2073         */
2074        
2075         Roo.log('pass click event');
2076         
2077         t.onClick(e);
2078         
2079         this.fireEvent("click", this, t, e);
2080         
2081         var _this = this;
2082         
2083         (function() { _this.hide(); }).defer(500);
2084     },
2085     
2086     onMouseOver : function(e){
2087         var t  = this.findTargetItem(e);
2088         //Roo.log(t);
2089         //if(t){
2090         //    if(t.canActivate && !t.disabled){
2091         //        this.setActiveItem(t, true);
2092         //    }
2093         //}
2094         
2095         this.fireEvent("mouseover", this, e, t);
2096     },
2097     isVisible : function(){
2098         return !this.hidden;
2099     },
2100      onMouseOut : function(e){
2101         var t  = this.findTargetItem(e);
2102         
2103         //if(t ){
2104         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2105         //        this.activeItem.deactivate();
2106         //        delete this.activeItem;
2107         //    }
2108         //}
2109         this.fireEvent("mouseout", this, e, t);
2110     },
2111     
2112     
2113     /**
2114      * Displays this menu relative to another element
2115      * @param {String/HTMLElement/Roo.Element} element The element to align to
2116      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2117      * the element (defaults to this.defaultAlign)
2118      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2119      */
2120     show : function(el, pos, parentMenu){
2121         this.parentMenu = parentMenu;
2122         if(!this.el){
2123             this.render();
2124         }
2125         this.fireEvent("beforeshow", this);
2126         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2127     },
2128      /**
2129      * Displays this menu at a specific xy position
2130      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2131      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2132      */
2133     showAt : function(xy, parentMenu, /* private: */_e){
2134         this.parentMenu = parentMenu;
2135         if(!this.el){
2136             this.render();
2137         }
2138         if(_e !== false){
2139             this.fireEvent("beforeshow", this);
2140             //xy = this.el.adjustForConstraints(xy);
2141         }
2142         
2143         //this.el.show();
2144         this.hideMenuItems();
2145         this.hidden = false;
2146         this.triggerEl.addClass('open');
2147         
2148         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2149             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2150         }
2151         
2152         if(this.el.getStyle('top').slice(-1) != "%"){
2153             this.el.setXY(xy);
2154         }
2155         
2156         this.focus();
2157         this.fireEvent("show", this);
2158     },
2159     
2160     focus : function(){
2161         return;
2162         if(!this.hidden){
2163             this.doFocus.defer(50, this);
2164         }
2165     },
2166
2167     doFocus : function(){
2168         if(!this.hidden){
2169             this.focusEl.focus();
2170         }
2171     },
2172
2173     /**
2174      * Hides this menu and optionally all parent menus
2175      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2176      */
2177     hide : function(deep)
2178     {
2179         
2180         this.hideMenuItems();
2181         if(this.el && this.isVisible()){
2182             this.fireEvent("beforehide", this);
2183             if(this.activeItem){
2184                 this.activeItem.deactivate();
2185                 this.activeItem = null;
2186             }
2187             this.triggerEl.removeClass('open');;
2188             this.hidden = true;
2189             this.fireEvent("hide", this);
2190         }
2191         if(deep === true && this.parentMenu){
2192             this.parentMenu.hide(true);
2193         }
2194     },
2195     
2196     onTriggerClick : function(e)
2197     {
2198         Roo.log('trigger click');
2199         
2200         var target = e.getTarget();
2201         
2202         Roo.log(target.nodeName.toLowerCase());
2203         
2204         if(target.nodeName.toLowerCase() === 'i'){
2205             e.preventDefault();
2206         }
2207         
2208     },
2209     
2210     onTriggerPress  : function(e)
2211     {
2212         Roo.log('trigger press');
2213         //Roo.log(e.getTarget());
2214        // Roo.log(this.triggerEl.dom);
2215        
2216         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2217         var pel = Roo.get(e.getTarget());
2218         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2219             Roo.log('is treeview or dropdown?');
2220             return;
2221         }
2222         
2223         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2224             return;
2225         }
2226         
2227         if (this.isVisible()) {
2228             Roo.log('hide');
2229             this.hide();
2230         } else {
2231             Roo.log('show');
2232             this.show(this.triggerEl, false, false);
2233         }
2234         
2235         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2236             e.stopEvent();
2237         }
2238         
2239     },
2240        
2241     
2242     hideMenuItems : function()
2243     {
2244         Roo.log("hide Menu Items");
2245         if (!this.el) { 
2246             return;
2247         }
2248         //$(backdrop).remove()
2249         this.el.select('.open',true).each(function(aa) {
2250             
2251             aa.removeClass('open');
2252           //var parent = getParent($(this))
2253           //var relatedTarget = { relatedTarget: this }
2254           
2255            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2256           //if (e.isDefaultPrevented()) return
2257            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2258         });
2259     },
2260     addxtypeChild : function (tree, cntr) {
2261         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2262           
2263         this.menuitems.add(comp);
2264         return comp;
2265
2266     },
2267     getEl : function()
2268     {
2269         Roo.log(this.el);
2270         return this.el;
2271     }
2272 });
2273
2274  
2275  /*
2276  * - LGPL
2277  *
2278  * menu item
2279  * 
2280  */
2281
2282
2283 /**
2284  * @class Roo.bootstrap.MenuItem
2285  * @extends Roo.bootstrap.Component
2286  * Bootstrap MenuItem class
2287  * @cfg {String} html the menu label
2288  * @cfg {String} href the link
2289  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2290  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2291  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2292  * @cfg {String} fa favicon to show on left of menu item.
2293  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2294  * 
2295  * 
2296  * @constructor
2297  * Create a new MenuItem
2298  * @param {Object} config The config object
2299  */
2300
2301
2302 Roo.bootstrap.MenuItem = function(config){
2303     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2304     this.addEvents({
2305         // raw events
2306         /**
2307          * @event click
2308          * The raw click event for the entire grid.
2309          * @param {Roo.bootstrap.MenuItem} this
2310          * @param {Roo.EventObject} e
2311          */
2312         "click" : true
2313     });
2314 };
2315
2316 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2317     
2318     href : false,
2319     html : false,
2320     preventDefault: true,
2321     isContainer : false,
2322     active : false,
2323     fa: false,
2324     
2325     getAutoCreate : function(){
2326         
2327         if(this.isContainer){
2328             return {
2329                 tag: 'li',
2330                 cls: 'dropdown-menu-item'
2331             };
2332         }
2333         var ctag = {
2334             tag: 'span',
2335             html: 'Link'
2336         };
2337         
2338         var anc = {
2339             tag : 'a',
2340             href : '#',
2341             cn : [  ]
2342         };
2343         
2344         if (this.fa !== false) {
2345             anc.cn.push({
2346                 tag : 'i',
2347                 cls : 'fa fa-' + this.fa
2348             });
2349         }
2350         
2351         anc.cn.push(ctag);
2352         
2353         
2354         var cfg= {
2355             tag: 'li',
2356             cls: 'dropdown-menu-item',
2357             cn: [ anc ]
2358         };
2359         if (this.parent().type == 'treeview') {
2360             cfg.cls = 'treeview-menu';
2361         }
2362         if (this.active) {
2363             cfg.cls += ' active';
2364         }
2365         
2366         
2367         
2368         anc.href = this.href || cfg.cn[0].href ;
2369         ctag.html = this.html || cfg.cn[0].html ;
2370         return cfg;
2371     },
2372     
2373     initEvents: function()
2374     {
2375         if (this.parent().type == 'treeview') {
2376             this.el.select('a').on('click', this.onClick, this);
2377         }
2378         if (this.menu) {
2379             this.menu.parentType = this.xtype;
2380             this.menu.triggerEl = this.el;
2381             this.menu = this.addxtype(Roo.apply({}, this.menu));
2382         }
2383         
2384     },
2385     onClick : function(e)
2386     {
2387         Roo.log('item on click ');
2388         //if(this.preventDefault){
2389         //    e.preventDefault();
2390         //}
2391         //this.parent().hideMenuItems();
2392         
2393         this.fireEvent('click', this, e);
2394     },
2395     getEl : function()
2396     {
2397         return this.el;
2398     } 
2399 });
2400
2401  
2402
2403  /*
2404  * - LGPL
2405  *
2406  * menu separator
2407  * 
2408  */
2409
2410
2411 /**
2412  * @class Roo.bootstrap.MenuSeparator
2413  * @extends Roo.bootstrap.Component
2414  * Bootstrap MenuSeparator class
2415  * 
2416  * @constructor
2417  * Create a new MenuItem
2418  * @param {Object} config The config object
2419  */
2420
2421
2422 Roo.bootstrap.MenuSeparator = function(config){
2423     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2424 };
2425
2426 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2427     
2428     getAutoCreate : function(){
2429         var cfg = {
2430             cls: 'divider',
2431             tag : 'li'
2432         };
2433         
2434         return cfg;
2435     }
2436    
2437 });
2438
2439  
2440
2441  
2442 /*
2443 * Licence: LGPL
2444 */
2445
2446 /**
2447  * @class Roo.bootstrap.Modal
2448  * @extends Roo.bootstrap.Component
2449  * Bootstrap Modal class
2450  * @cfg {String} title Title of dialog
2451  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2452  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2453  * @cfg {Boolean} specificTitle default false
2454  * @cfg {Array} buttons Array of buttons or standard button set..
2455  * @cfg {String} buttonPosition (left|right|center) default right
2456  * @cfg {Boolean} animate default true
2457  * @cfg {Boolean} allow_close default true
2458  * 
2459  * @constructor
2460  * Create a new Modal Dialog
2461  * @param {Object} config The config object
2462  */
2463
2464 Roo.bootstrap.Modal = function(config){
2465     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2466     this.addEvents({
2467         // raw events
2468         /**
2469          * @event btnclick
2470          * The raw btnclick event for the button
2471          * @param {Roo.EventObject} e
2472          */
2473         "btnclick" : true
2474     });
2475     this.buttons = this.buttons || [];
2476      
2477     if (this.tmpl) {
2478         this.tmpl = Roo.factory(this.tmpl);
2479     }
2480     
2481 };
2482
2483 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2484     
2485     title : 'test dialog',
2486    
2487     buttons : false,
2488     
2489     // set on load...
2490      
2491     html: false,
2492     
2493     tmp: false,
2494     
2495     specificTitle: false,
2496     
2497     buttonPosition: 'right',
2498     
2499     allow_close : true,
2500     
2501     animate : true,
2502     
2503     
2504      // private
2505     bodyEl:  false,
2506     footerEl:  false,
2507     titleEl:  false,
2508     closeEl:  false,
2509     
2510     
2511     onRender : function(ct, position)
2512     {
2513         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2514      
2515         if(!this.el){
2516             var cfg = Roo.apply({},  this.getAutoCreate());
2517             cfg.id = Roo.id();
2518             //if(!cfg.name){
2519             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2520             //}
2521             //if (!cfg.name.length) {
2522             //    delete cfg.name;
2523            // }
2524             if (this.cls) {
2525                 cfg.cls += ' ' + this.cls;
2526             }
2527             if (this.style) {
2528                 cfg.style = this.style;
2529             }
2530             this.el = Roo.get(document.body).createChild(cfg, position);
2531         }
2532         //var type = this.el.dom.type;
2533         
2534         
2535         if(this.tabIndex !== undefined){
2536             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2537         }
2538         
2539         
2540         this.bodyEl = this.el.select('.modal-body',true).first();
2541         this.closeEl = this.el.select('.modal-header .close', true).first();
2542         this.footerEl = this.el.select('.modal-footer',true).first();
2543         this.titleEl = this.el.select('.modal-title',true).first();
2544         
2545         
2546          
2547         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2548         this.maskEl.enableDisplayMode("block");
2549         this.maskEl.hide();
2550         //this.el.addClass("x-dlg-modal");
2551     
2552         if (this.buttons.length) {
2553             Roo.each(this.buttons, function(bb) {
2554                 var b = Roo.apply({}, bb);
2555                 b.xns = b.xns || Roo.bootstrap;
2556                 b.xtype = b.xtype || 'Button';
2557                 if (typeof(b.listeners) == 'undefined') {
2558                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2559                 }
2560                 
2561                 var btn = Roo.factory(b);
2562                 
2563                 btn.render(this.el.select('.modal-footer div').first());
2564                 
2565             },this);
2566         }
2567         // render the children.
2568         var nitems = [];
2569         
2570         if(typeof(this.items) != 'undefined'){
2571             var items = this.items;
2572             delete this.items;
2573
2574             for(var i =0;i < items.length;i++) {
2575                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2576             }
2577         }
2578         
2579         this.items = nitems;
2580         
2581         // where are these used - they used to be body/close/footer
2582         
2583        
2584         this.initEvents();
2585         //this.el.addClass([this.fieldClass, this.cls]);
2586         
2587     },
2588     
2589     getAutoCreate : function(){
2590         
2591         
2592         var bdy = {
2593                 cls : 'modal-body',
2594                 html : this.html || ''
2595         };
2596         
2597         var title = {
2598             tag: 'h4',
2599             cls : 'modal-title',
2600             html : this.title
2601         };
2602         
2603         if(this.specificTitle){
2604             title = this.title;
2605             
2606         };
2607         
2608         var header = [];
2609         if (this.allow_close) {
2610             header.push({
2611                 tag: 'button',
2612                 cls : 'close',
2613                 html : '&times'
2614             });
2615         }
2616         header.push(title);
2617         
2618         var modal = {
2619             cls: "modal",
2620             style : 'display: none',
2621             cn : [
2622                 {
2623                     cls: "modal-dialog",
2624                     cn : [
2625                         {
2626                             cls : "modal-content",
2627                             cn : [
2628                                 {
2629                                     cls : 'modal-header',
2630                                     cn : header
2631                                 },
2632                                 bdy,
2633                                 {
2634                                     cls : 'modal-footer',
2635                                     cn : [
2636                                         {
2637                                             tag: 'div',
2638                                             cls: 'btn-' + this.buttonPosition
2639                                         }
2640                                     ]
2641                                     
2642                                 }
2643                                 
2644                                 
2645                             ]
2646                             
2647                         }
2648                     ]
2649                         
2650                 }
2651             ]
2652         };
2653         
2654         if(this.animate){
2655             modal.cls += ' fade';
2656         }
2657         
2658         return modal;
2659           
2660     },
2661     getChildContainer : function() {
2662          
2663          return this.bodyEl;
2664         
2665     },
2666     getButtonContainer : function() {
2667          return this.el.select('.modal-footer div',true).first();
2668         
2669     },
2670     initEvents : function()
2671     {
2672         if (this.allow_close) {
2673             this.closeEl.on('click', this.hide, this);
2674         }
2675         
2676         var _this = this;
2677         
2678         window.addEventListener("resize", function() { _this.resize(); } );
2679
2680     },
2681     
2682     resize : function()
2683     {
2684         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2685     },
2686     
2687     show : function() {
2688         
2689         if (!this.rendered) {
2690             this.render();
2691         }
2692         
2693         this.el.setStyle('display', 'block');
2694         
2695         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2696             var _this = this;
2697             (function(){
2698                 this.el.addClass('in');
2699             }).defer(50, this);
2700         }else{
2701             this.el.addClass('in');
2702             
2703         }
2704         
2705         // not sure how we can show data in here.. 
2706         //if (this.tmpl) {
2707         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2708         //}
2709         
2710         Roo.get(document.body).addClass("x-body-masked");
2711         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2712         this.maskEl.show();
2713         this.el.setStyle('zIndex', '10001');
2714        
2715         this.fireEvent('show', this);
2716          
2717         
2718         
2719     },
2720     hide : function()
2721     {
2722         this.maskEl.hide();
2723         Roo.get(document.body).removeClass("x-body-masked");
2724         this.el.removeClass('in');
2725         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2726         
2727         if(this.animate){ // why
2728             var _this = this;
2729             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2730         }else{
2731             this.el.setStyle('display', 'none');
2732         }
2733         
2734         this.fireEvent('hide', this);
2735     },
2736     
2737     addButton : function(str, cb)
2738     {
2739          
2740         
2741         var b = Roo.apply({}, { html : str } );
2742         b.xns = b.xns || Roo.bootstrap;
2743         b.xtype = b.xtype || 'Button';
2744         if (typeof(b.listeners) == 'undefined') {
2745             b.listeners = { click : cb.createDelegate(this)  };
2746         }
2747         
2748         var btn = Roo.factory(b);
2749            
2750         btn.render(this.el.select('.modal-footer div').first());
2751         
2752         return btn;   
2753        
2754     },
2755     
2756     setDefaultButton : function(btn)
2757     {
2758         //this.el.select('.modal-footer').()
2759     },
2760     diff : false,
2761     
2762     resizeTo: function(w,h)
2763     {
2764         // skip.. ?? why??
2765         
2766         this.el.select('.modal-dialog',true).first().setWidth(w);
2767         if (this.diff === false) {
2768             this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2769         }
2770         
2771         this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2772         
2773         
2774     },
2775     setContentSize  : function(w, h)
2776     {
2777         
2778     },
2779     onButtonClick: function(btn,e)
2780     {
2781         //Roo.log([a,b,c]);
2782         this.fireEvent('btnclick', btn.name, e);
2783     },
2784      /**
2785      * Set the title of the Dialog
2786      * @param {String} str new Title
2787      */
2788     setTitle: function(str) {
2789         this.titleEl.dom.innerHTML = str;    
2790     },
2791     /**
2792      * Set the body of the Dialog
2793      * @param {String} str new Title
2794      */
2795     setBody: function(str) {
2796         this.bodyEl.dom.innerHTML = str;    
2797     },
2798     /**
2799      * Set the body of the Dialog using the template
2800      * @param {Obj} data - apply this data to the template and replace the body contents.
2801      */
2802     applyBody: function(obj)
2803     {
2804         if (!this.tmpl) {
2805             Roo.log("Error - using apply Body without a template");
2806             //code
2807         }
2808         this.tmpl.overwrite(this.bodyEl, obj);
2809     }
2810     
2811 });
2812
2813
2814 Roo.apply(Roo.bootstrap.Modal,  {
2815     /**
2816          * Button config that displays a single OK button
2817          * @type Object
2818          */
2819         OK :  [{
2820             name : 'ok',
2821             weight : 'primary',
2822             html : 'OK'
2823         }], 
2824         /**
2825          * Button config that displays Yes and No buttons
2826          * @type Object
2827          */
2828         YESNO : [
2829             {
2830                 name  : 'no',
2831                 html : 'No'
2832             },
2833             {
2834                 name  :'yes',
2835                 weight : 'primary',
2836                 html : 'Yes'
2837             }
2838         ],
2839         
2840         /**
2841          * Button config that displays OK and Cancel buttons
2842          * @type Object
2843          */
2844         OKCANCEL : [
2845             {
2846                name : 'cancel',
2847                 html : 'Cancel'
2848             },
2849             {
2850                 name : 'ok',
2851                 weight : 'primary',
2852                 html : 'OK'
2853             }
2854         ],
2855         /**
2856          * Button config that displays Yes, No and Cancel buttons
2857          * @type Object
2858          */
2859         YESNOCANCEL : [
2860             {
2861                 name : 'yes',
2862                 weight : 'primary',
2863                 html : 'Yes'
2864             },
2865             {
2866                 name : 'no',
2867                 html : 'No'
2868             },
2869             {
2870                 name : 'cancel',
2871                 html : 'Cancel'
2872             }
2873         ]
2874 });
2875  
2876  /*
2877  * - LGPL
2878  *
2879  * messagebox - can be used as a replace
2880  * 
2881  */
2882 /**
2883  * @class Roo.MessageBox
2884  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2885  * Example usage:
2886  *<pre><code>
2887 // Basic alert:
2888 Roo.Msg.alert('Status', 'Changes saved successfully.');
2889
2890 // Prompt for user data:
2891 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2892     if (btn == 'ok'){
2893         // process text value...
2894     }
2895 });
2896
2897 // Show a dialog using config options:
2898 Roo.Msg.show({
2899    title:'Save Changes?',
2900    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2901    buttons: Roo.Msg.YESNOCANCEL,
2902    fn: processResult,
2903    animEl: 'elId'
2904 });
2905 </code></pre>
2906  * @singleton
2907  */
2908 Roo.bootstrap.MessageBox = function(){
2909     var dlg, opt, mask, waitTimer;
2910     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2911     var buttons, activeTextEl, bwidth;
2912
2913     
2914     // private
2915     var handleButton = function(button){
2916         dlg.hide();
2917         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2918     };
2919
2920     // private
2921     var handleHide = function(){
2922         if(opt && opt.cls){
2923             dlg.el.removeClass(opt.cls);
2924         }
2925         //if(waitTimer){
2926         //    Roo.TaskMgr.stop(waitTimer);
2927         //    waitTimer = null;
2928         //}
2929     };
2930
2931     // private
2932     var updateButtons = function(b){
2933         var width = 0;
2934         if(!b){
2935             buttons["ok"].hide();
2936             buttons["cancel"].hide();
2937             buttons["yes"].hide();
2938             buttons["no"].hide();
2939             //dlg.footer.dom.style.display = 'none';
2940             return width;
2941         }
2942         dlg.footerEl.dom.style.display = '';
2943         for(var k in buttons){
2944             if(typeof buttons[k] != "function"){
2945                 if(b[k]){
2946                     buttons[k].show();
2947                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2948                     width += buttons[k].el.getWidth()+15;
2949                 }else{
2950                     buttons[k].hide();
2951                 }
2952             }
2953         }
2954         return width;
2955     };
2956
2957     // private
2958     var handleEsc = function(d, k, e){
2959         if(opt && opt.closable !== false){
2960             dlg.hide();
2961         }
2962         if(e){
2963             e.stopEvent();
2964         }
2965     };
2966
2967     return {
2968         /**
2969          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2970          * @return {Roo.BasicDialog} The BasicDialog element
2971          */
2972         getDialog : function(){
2973            if(!dlg){
2974                 dlg = new Roo.bootstrap.Modal( {
2975                     //draggable: true,
2976                     //resizable:false,
2977                     //constraintoviewport:false,
2978                     //fixedcenter:true,
2979                     //collapsible : false,
2980                     //shim:true,
2981                     //modal: true,
2982                   //  width:400,
2983                   //  height:100,
2984                     //buttonAlign:"center",
2985                     closeClick : function(){
2986                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2987                             handleButton("no");
2988                         }else{
2989                             handleButton("cancel");
2990                         }
2991                     }
2992                 });
2993                 dlg.render();
2994                 dlg.on("hide", handleHide);
2995                 mask = dlg.mask;
2996                 //dlg.addKeyListener(27, handleEsc);
2997                 buttons = {};
2998                 this.buttons = buttons;
2999                 var bt = this.buttonText;
3000                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3001                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3002                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3003                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3004                 //Roo.log(buttons);
3005                 bodyEl = dlg.bodyEl.createChild({
3006
3007                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3008                         '<textarea class="roo-mb-textarea"></textarea>' +
3009                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3010                 });
3011                 msgEl = bodyEl.dom.firstChild;
3012                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3013                 textboxEl.enableDisplayMode();
3014                 textboxEl.addKeyListener([10,13], function(){
3015                     if(dlg.isVisible() && opt && opt.buttons){
3016                         if(opt.buttons.ok){
3017                             handleButton("ok");
3018                         }else if(opt.buttons.yes){
3019                             handleButton("yes");
3020                         }
3021                     }
3022                 });
3023                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3024                 textareaEl.enableDisplayMode();
3025                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3026                 progressEl.enableDisplayMode();
3027                 var pf = progressEl.dom.firstChild;
3028                 if (pf) {
3029                     pp = Roo.get(pf.firstChild);
3030                     pp.setHeight(pf.offsetHeight);
3031                 }
3032                 
3033             }
3034             return dlg;
3035         },
3036
3037         /**
3038          * Updates the message box body text
3039          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3040          * the XHTML-compliant non-breaking space character '&amp;#160;')
3041          * @return {Roo.MessageBox} This message box
3042          */
3043         updateText : function(text){
3044             if(!dlg.isVisible() && !opt.width){
3045                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3046             }
3047             msgEl.innerHTML = text || '&#160;';
3048       
3049             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3050             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3051             var w = Math.max(
3052                     Math.min(opt.width || cw , this.maxWidth), 
3053                     Math.max(opt.minWidth || this.minWidth, bwidth)
3054             );
3055             if(opt.prompt){
3056                 activeTextEl.setWidth(w);
3057             }
3058             if(dlg.isVisible()){
3059                 dlg.fixedcenter = false;
3060             }
3061             // to big, make it scroll. = But as usual stupid IE does not support
3062             // !important..
3063             
3064             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3065                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3066                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3067             } else {
3068                 bodyEl.dom.style.height = '';
3069                 bodyEl.dom.style.overflowY = '';
3070             }
3071             if (cw > w) {
3072                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3073             } else {
3074                 bodyEl.dom.style.overflowX = '';
3075             }
3076             
3077             dlg.setContentSize(w, bodyEl.getHeight());
3078             if(dlg.isVisible()){
3079                 dlg.fixedcenter = true;
3080             }
3081             return this;
3082         },
3083
3084         /**
3085          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3086          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3087          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3088          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3089          * @return {Roo.MessageBox} This message box
3090          */
3091         updateProgress : function(value, text){
3092             if(text){
3093                 this.updateText(text);
3094             }
3095             if (pp) { // weird bug on my firefox - for some reason this is not defined
3096                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3097             }
3098             return this;
3099         },        
3100
3101         /**
3102          * Returns true if the message box is currently displayed
3103          * @return {Boolean} True if the message box is visible, else false
3104          */
3105         isVisible : function(){
3106             return dlg && dlg.isVisible();  
3107         },
3108
3109         /**
3110          * Hides the message box if it is displayed
3111          */
3112         hide : function(){
3113             if(this.isVisible()){
3114                 dlg.hide();
3115             }  
3116         },
3117
3118         /**
3119          * Displays a new message box, or reinitializes an existing message box, based on the config options
3120          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3121          * The following config object properties are supported:
3122          * <pre>
3123 Property    Type             Description
3124 ----------  ---------------  ------------------------------------------------------------------------------------
3125 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3126                                    closes (defaults to undefined)
3127 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3128                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3129 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3130                                    progress and wait dialogs will ignore this property and always hide the
3131                                    close button as they can only be closed programmatically.
3132 cls               String           A custom CSS class to apply to the message box element
3133 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3134                                    displayed (defaults to 75)
3135 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3136                                    function will be btn (the name of the button that was clicked, if applicable,
3137                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3138                                    Progress and wait dialogs will ignore this option since they do not respond to
3139                                    user actions and can only be closed programmatically, so any required function
3140                                    should be called by the same code after it closes the dialog.
3141 icon              String           A CSS class that provides a background image to be used as an icon for
3142                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3143 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3144 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3145 modal             Boolean          False to allow user interaction with the page while the message box is
3146                                    displayed (defaults to true)
3147 msg               String           A string that will replace the existing message box body text (defaults
3148                                    to the XHTML-compliant non-breaking space character '&#160;')
3149 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3150 progress          Boolean          True to display a progress bar (defaults to false)
3151 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3152 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3153 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3154 title             String           The title text
3155 value             String           The string value to set into the active textbox element if displayed
3156 wait              Boolean          True to display a progress bar (defaults to false)
3157 width             Number           The width of the dialog in pixels
3158 </pre>
3159          *
3160          * Example usage:
3161          * <pre><code>
3162 Roo.Msg.show({
3163    title: 'Address',
3164    msg: 'Please enter your address:',
3165    width: 300,
3166    buttons: Roo.MessageBox.OKCANCEL,
3167    multiline: true,
3168    fn: saveAddress,
3169    animEl: 'addAddressBtn'
3170 });
3171 </code></pre>
3172          * @param {Object} config Configuration options
3173          * @return {Roo.MessageBox} This message box
3174          */
3175         show : function(options)
3176         {
3177             
3178             // this causes nightmares if you show one dialog after another
3179             // especially on callbacks..
3180              
3181             if(this.isVisible()){
3182                 
3183                 this.hide();
3184                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3185                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3186                 Roo.log("New Dialog Message:" +  options.msg )
3187                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3188                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3189                 
3190             }
3191             var d = this.getDialog();
3192             opt = options;
3193             d.setTitle(opt.title || "&#160;");
3194             d.closeEl.setDisplayed(opt.closable !== false);
3195             activeTextEl = textboxEl;
3196             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3197             if(opt.prompt){
3198                 if(opt.multiline){
3199                     textboxEl.hide();
3200                     textareaEl.show();
3201                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3202                         opt.multiline : this.defaultTextHeight);
3203                     activeTextEl = textareaEl;
3204                 }else{
3205                     textboxEl.show();
3206                     textareaEl.hide();
3207                 }
3208             }else{
3209                 textboxEl.hide();
3210                 textareaEl.hide();
3211             }
3212             progressEl.setDisplayed(opt.progress === true);
3213             this.updateProgress(0);
3214             activeTextEl.dom.value = opt.value || "";
3215             if(opt.prompt){
3216                 dlg.setDefaultButton(activeTextEl);
3217             }else{
3218                 var bs = opt.buttons;
3219                 var db = null;
3220                 if(bs && bs.ok){
3221                     db = buttons["ok"];
3222                 }else if(bs && bs.yes){
3223                     db = buttons["yes"];
3224                 }
3225                 dlg.setDefaultButton(db);
3226             }
3227             bwidth = updateButtons(opt.buttons);
3228             this.updateText(opt.msg);
3229             if(opt.cls){
3230                 d.el.addClass(opt.cls);
3231             }
3232             d.proxyDrag = opt.proxyDrag === true;
3233             d.modal = opt.modal !== false;
3234             d.mask = opt.modal !== false ? mask : false;
3235             if(!d.isVisible()){
3236                 // force it to the end of the z-index stack so it gets a cursor in FF
3237                 document.body.appendChild(dlg.el.dom);
3238                 d.animateTarget = null;
3239                 d.show(options.animEl);
3240             }
3241             return this;
3242         },
3243
3244         /**
3245          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3246          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3247          * and closing the message box when the process is complete.
3248          * @param {String} title The title bar text
3249          * @param {String} msg The message box body text
3250          * @return {Roo.MessageBox} This message box
3251          */
3252         progress : function(title, msg){
3253             this.show({
3254                 title : title,
3255                 msg : msg,
3256                 buttons: false,
3257                 progress:true,
3258                 closable:false,
3259                 minWidth: this.minProgressWidth,
3260                 modal : true
3261             });
3262             return this;
3263         },
3264
3265         /**
3266          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3267          * If a callback function is passed it will be called after the user clicks the button, and the
3268          * id of the button that was clicked will be passed as the only parameter to the callback
3269          * (could also be the top-right close button).
3270          * @param {String} title The title bar text
3271          * @param {String} msg The message box body text
3272          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3273          * @param {Object} scope (optional) The scope of the callback function
3274          * @return {Roo.MessageBox} This message box
3275          */
3276         alert : function(title, msg, fn, scope){
3277             this.show({
3278                 title : title,
3279                 msg : msg,
3280                 buttons: this.OK,
3281                 fn: fn,
3282                 scope : scope,
3283                 modal : true
3284             });
3285             return this;
3286         },
3287
3288         /**
3289          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3290          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3291          * You are responsible for closing the message box when the process is complete.
3292          * @param {String} msg The message box body text
3293          * @param {String} title (optional) The title bar text
3294          * @return {Roo.MessageBox} This message box
3295          */
3296         wait : function(msg, title){
3297             this.show({
3298                 title : title,
3299                 msg : msg,
3300                 buttons: false,
3301                 closable:false,
3302                 progress:true,
3303                 modal:true,
3304                 width:300,
3305                 wait:true
3306             });
3307             waitTimer = Roo.TaskMgr.start({
3308                 run: function(i){
3309                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3310                 },
3311                 interval: 1000
3312             });
3313             return this;
3314         },
3315
3316         /**
3317          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3318          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3319          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3320          * @param {String} title The title bar text
3321          * @param {String} msg The message box body text
3322          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3323          * @param {Object} scope (optional) The scope of the callback function
3324          * @return {Roo.MessageBox} This message box
3325          */
3326         confirm : function(title, msg, fn, scope){
3327             this.show({
3328                 title : title,
3329                 msg : msg,
3330                 buttons: this.YESNO,
3331                 fn: fn,
3332                 scope : scope,
3333                 modal : true
3334             });
3335             return this;
3336         },
3337
3338         /**
3339          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3340          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3341          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3342          * (could also be the top-right close button) and the text that was entered will be passed as the two
3343          * parameters to the callback.
3344          * @param {String} title The title bar text
3345          * @param {String} msg The message box body text
3346          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3347          * @param {Object} scope (optional) The scope of the callback function
3348          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3349          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3350          * @return {Roo.MessageBox} This message box
3351          */
3352         prompt : function(title, msg, fn, scope, multiline){
3353             this.show({
3354                 title : title,
3355                 msg : msg,
3356                 buttons: this.OKCANCEL,
3357                 fn: fn,
3358                 minWidth:250,
3359                 scope : scope,
3360                 prompt:true,
3361                 multiline: multiline,
3362                 modal : true
3363             });
3364             return this;
3365         },
3366
3367         /**
3368          * Button config that displays a single OK button
3369          * @type Object
3370          */
3371         OK : {ok:true},
3372         /**
3373          * Button config that displays Yes and No buttons
3374          * @type Object
3375          */
3376         YESNO : {yes:true, no:true},
3377         /**
3378          * Button config that displays OK and Cancel buttons
3379          * @type Object
3380          */
3381         OKCANCEL : {ok:true, cancel:true},
3382         /**
3383          * Button config that displays Yes, No and Cancel buttons
3384          * @type Object
3385          */
3386         YESNOCANCEL : {yes:true, no:true, cancel:true},
3387
3388         /**
3389          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3390          * @type Number
3391          */
3392         defaultTextHeight : 75,
3393         /**
3394          * The maximum width in pixels of the message box (defaults to 600)
3395          * @type Number
3396          */
3397         maxWidth : 600,
3398         /**
3399          * The minimum width in pixels of the message box (defaults to 100)
3400          * @type Number
3401          */
3402         minWidth : 100,
3403         /**
3404          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3405          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3406          * @type Number
3407          */
3408         minProgressWidth : 250,
3409         /**
3410          * An object containing the default button text strings that can be overriden for localized language support.
3411          * Supported properties are: ok, cancel, yes and no.
3412          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3413          * @type Object
3414          */
3415         buttonText : {
3416             ok : "OK",
3417             cancel : "Cancel",
3418             yes : "Yes",
3419             no : "No"
3420         }
3421     };
3422 }();
3423
3424 /**
3425  * Shorthand for {@link Roo.MessageBox}
3426  */
3427 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3428 Roo.Msg = Roo.Msg || Roo.MessageBox;
3429 /*
3430  * - LGPL
3431  *
3432  * navbar
3433  * 
3434  */
3435
3436 /**
3437  * @class Roo.bootstrap.Navbar
3438  * @extends Roo.bootstrap.Component
3439  * Bootstrap Navbar class
3440
3441  * @constructor
3442  * Create a new Navbar
3443  * @param {Object} config The config object
3444  */
3445
3446
3447 Roo.bootstrap.Navbar = function(config){
3448     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3449     this.addEvents({
3450         // raw events
3451         /**
3452          * @event beforetoggle
3453          * Fire before toggle the menu
3454          * @param {Roo.EventObject} e
3455          */
3456         "beforetoggle" : true
3457     });
3458 };
3459
3460 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3461     
3462     
3463    
3464     // private
3465     navItems : false,
3466     loadMask : false,
3467     
3468     
3469     getAutoCreate : function(){
3470         
3471         
3472         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3473         
3474     },
3475     
3476     initEvents :function ()
3477     {
3478         //Roo.log(this.el.select('.navbar-toggle',true));
3479         this.el.select('.navbar-toggle',true).on('click', function() {
3480             if(this.fireEvent('beforetoggle', this) !== false){
3481                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3482             }
3483             
3484         }, this);
3485         
3486         var mark = {
3487             tag: "div",
3488             cls:"x-dlg-mask"
3489         };
3490         
3491         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3492         
3493         var size = this.el.getSize();
3494         this.maskEl.setSize(size.width, size.height);
3495         this.maskEl.enableDisplayMode("block");
3496         this.maskEl.hide();
3497         
3498         if(this.loadMask){
3499             this.maskEl.show();
3500         }
3501     },
3502     
3503     
3504     getChildContainer : function()
3505     {
3506         if (this.el.select('.collapse').getCount()) {
3507             return this.el.select('.collapse',true).first();
3508         }
3509         
3510         return this.el;
3511     },
3512     
3513     mask : function()
3514     {
3515         this.maskEl.show();
3516     },
3517     
3518     unmask : function()
3519     {
3520         this.maskEl.hide();
3521     } 
3522     
3523     
3524     
3525     
3526 });
3527
3528
3529
3530  
3531
3532  /*
3533  * - LGPL
3534  *
3535  * navbar
3536  * 
3537  */
3538
3539 /**
3540  * @class Roo.bootstrap.NavSimplebar
3541  * @extends Roo.bootstrap.Navbar
3542  * Bootstrap Sidebar class
3543  *
3544  * @cfg {Boolean} inverse is inverted color
3545  * 
3546  * @cfg {String} type (nav | pills | tabs)
3547  * @cfg {Boolean} arrangement stacked | justified
3548  * @cfg {String} align (left | right) alignment
3549  * 
3550  * @cfg {Boolean} main (true|false) main nav bar? default false
3551  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3552  * 
3553  * @cfg {String} tag (header|footer|nav|div) default is nav 
3554
3555  * 
3556  * 
3557  * 
3558  * @constructor
3559  * Create a new Sidebar
3560  * @param {Object} config The config object
3561  */
3562
3563
3564 Roo.bootstrap.NavSimplebar = function(config){
3565     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3566 };
3567
3568 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3569     
3570     inverse: false,
3571     
3572     type: false,
3573     arrangement: '',
3574     align : false,
3575     
3576     
3577     
3578     main : false,
3579     
3580     
3581     tag : false,
3582     
3583     
3584     getAutoCreate : function(){
3585         
3586         
3587         var cfg = {
3588             tag : this.tag || 'div',
3589             cls : 'navbar'
3590         };
3591           
3592         
3593         cfg.cn = [
3594             {
3595                 cls: 'nav',
3596                 tag : 'ul'
3597             }
3598         ];
3599         
3600          
3601         this.type = this.type || 'nav';
3602         if (['tabs','pills'].indexOf(this.type)!==-1) {
3603             cfg.cn[0].cls += ' nav-' + this.type
3604         
3605         
3606         } else {
3607             if (this.type!=='nav') {
3608                 Roo.log('nav type must be nav/tabs/pills')
3609             }
3610             cfg.cn[0].cls += ' navbar-nav'
3611         }
3612         
3613         
3614         
3615         
3616         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3617             cfg.cn[0].cls += ' nav-' + this.arrangement;
3618         }
3619         
3620         
3621         if (this.align === 'right') {
3622             cfg.cn[0].cls += ' navbar-right';
3623         }
3624         
3625         if (this.inverse) {
3626             cfg.cls += ' navbar-inverse';
3627             
3628         }
3629         
3630         
3631         return cfg;
3632     
3633         
3634     }
3635     
3636     
3637     
3638 });
3639
3640
3641
3642  
3643
3644  
3645        /*
3646  * - LGPL
3647  *
3648  * navbar
3649  * 
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavHeaderbar
3654  * @extends Roo.bootstrap.NavSimplebar
3655  * Bootstrap Sidebar class
3656  *
3657  * @cfg {String} brand what is brand
3658  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3659  * @cfg {String} brand_href href of the brand
3660  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3661  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3662  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3663  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3664  * 
3665  * @constructor
3666  * Create a new Sidebar
3667  * @param {Object} config The config object
3668  */
3669
3670
3671 Roo.bootstrap.NavHeaderbar = function(config){
3672     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3673       
3674 };
3675
3676 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3677     
3678     position: '',
3679     brand: '',
3680     brand_href: false,
3681     srButton : true,
3682     autohide : false,
3683     desktopCenter : false,
3684    
3685     
3686     getAutoCreate : function(){
3687         
3688         var   cfg = {
3689             tag: this.nav || 'nav',
3690             cls: 'navbar',
3691             role: 'navigation',
3692             cn: []
3693         };
3694         
3695         var cn = cfg.cn;
3696         if (this.desktopCenter) {
3697             cn.push({cls : 'container', cn : []});
3698             cn = cn[0].cn;
3699         }
3700         
3701         if(this.srButton){
3702             cn.push({
3703                 tag: 'div',
3704                 cls: 'navbar-header',
3705                 cn: [
3706                     {
3707                         tag: 'button',
3708                         type: 'button',
3709                         cls: 'navbar-toggle',
3710                         'data-toggle': 'collapse',
3711                         cn: [
3712                             {
3713                                 tag: 'span',
3714                                 cls: 'sr-only',
3715                                 html: 'Toggle navigation'
3716                             },
3717                             {
3718                                 tag: 'span',
3719                                 cls: 'icon-bar'
3720                             },
3721                             {
3722                                 tag: 'span',
3723                                 cls: 'icon-bar'
3724                             },
3725                             {
3726                                 tag: 'span',
3727                                 cls: 'icon-bar'
3728                             }
3729                         ]
3730                     }
3731                 ]
3732             });
3733         }
3734         
3735         cn.push({
3736             tag: 'div',
3737             cls: 'collapse navbar-collapse',
3738             cn : []
3739         });
3740         
3741         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3742         
3743         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3744             cfg.cls += ' navbar-' + this.position;
3745             
3746             // tag can override this..
3747             
3748             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3749         }
3750         
3751         if (this.brand !== '') {
3752             cn[0].cn.push({
3753                 tag: 'a',
3754                 href: this.brand_href ? this.brand_href : '#',
3755                 cls: 'navbar-brand',
3756                 cn: [
3757                 this.brand
3758                 ]
3759             });
3760         }
3761         
3762         if(this.main){
3763             cfg.cls += ' main-nav';
3764         }
3765         
3766         
3767         return cfg;
3768
3769         
3770     },
3771     getHeaderChildContainer : function()
3772     {
3773         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3774             return this.el.select('.navbar-header',true).first();
3775         }
3776         
3777         return this.getChildContainer();
3778     },
3779     
3780     
3781     initEvents : function()
3782     {
3783         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3784         
3785         if (this.autohide) {
3786             
3787             var prevScroll = 0;
3788             var ft = this.el;
3789             
3790             Roo.get(document).on('scroll',function(e) {
3791                 var ns = Roo.get(document).getScroll().top;
3792                 var os = prevScroll;
3793                 prevScroll = ns;
3794                 
3795                 if(ns > os){
3796                     ft.removeClass('slideDown');
3797                     ft.addClass('slideUp');
3798                     return;
3799                 }
3800                 ft.removeClass('slideUp');
3801                 ft.addClass('slideDown');
3802                  
3803               
3804           },this);
3805         }
3806     }    
3807     
3808 });
3809
3810
3811
3812  
3813
3814  /*
3815  * - LGPL
3816  *
3817  * navbar
3818  * 
3819  */
3820
3821 /**
3822  * @class Roo.bootstrap.NavSidebar
3823  * @extends Roo.bootstrap.Navbar
3824  * Bootstrap Sidebar class
3825  * 
3826  * @constructor
3827  * Create a new Sidebar
3828  * @param {Object} config The config object
3829  */
3830
3831
3832 Roo.bootstrap.NavSidebar = function(config){
3833     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3834 };
3835
3836 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3837     
3838     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3839     
3840     getAutoCreate : function(){
3841         
3842         
3843         return  {
3844             tag: 'div',
3845             cls: 'sidebar sidebar-nav'
3846         };
3847     
3848         
3849     }
3850     
3851     
3852     
3853 });
3854
3855
3856
3857  
3858
3859  /*
3860  * - LGPL
3861  *
3862  * nav group
3863  * 
3864  */
3865
3866 /**
3867  * @class Roo.bootstrap.NavGroup
3868  * @extends Roo.bootstrap.Component
3869  * Bootstrap NavGroup class
3870  * @cfg {String} align (left|right)
3871  * @cfg {Boolean} inverse
3872  * @cfg {String} type (nav|pills|tab) default nav
3873  * @cfg {String} navId - reference Id for navbar.
3874
3875  * 
3876  * @constructor
3877  * Create a new nav group
3878  * @param {Object} config The config object
3879  */
3880
3881 Roo.bootstrap.NavGroup = function(config){
3882     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3883     this.navItems = [];
3884    
3885     Roo.bootstrap.NavGroup.register(this);
3886      this.addEvents({
3887         /**
3888              * @event changed
3889              * Fires when the active item changes
3890              * @param {Roo.bootstrap.NavGroup} this
3891              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3892              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3893          */
3894         'changed': true
3895      });
3896     
3897 };
3898
3899 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3900     
3901     align: '',
3902     inverse: false,
3903     form: false,
3904     type: 'nav',
3905     navId : '',
3906     // private
3907     
3908     navItems : false, 
3909     
3910     getAutoCreate : function()
3911     {
3912         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3913         
3914         cfg = {
3915             tag : 'ul',
3916             cls: 'nav' 
3917         };
3918         
3919         if (['tabs','pills'].indexOf(this.type)!==-1) {
3920             cfg.cls += ' nav-' + this.type
3921         } else {
3922             if (this.type!=='nav') {
3923                 Roo.log('nav type must be nav/tabs/pills')
3924             }
3925             cfg.cls += ' navbar-nav'
3926         }
3927         
3928         if (this.parent().sidebar) {
3929             cfg = {
3930                 tag: 'ul',
3931                 cls: 'dashboard-menu sidebar-menu'
3932             };
3933             
3934             return cfg;
3935         }
3936         
3937         if (this.form === true) {
3938             cfg = {
3939                 tag: 'form',
3940                 cls: 'navbar-form'
3941             };
3942             
3943             if (this.align === 'right') {
3944                 cfg.cls += ' navbar-right';
3945             } else {
3946                 cfg.cls += ' navbar-left';
3947             }
3948         }
3949         
3950         if (this.align === 'right') {
3951             cfg.cls += ' navbar-right';
3952         }
3953         
3954         if (this.inverse) {
3955             cfg.cls += ' navbar-inverse';
3956             
3957         }
3958         
3959         
3960         return cfg;
3961     },
3962     /**
3963     * sets the active Navigation item
3964     * @param {Roo.bootstrap.NavItem} the new current navitem
3965     */
3966     setActiveItem : function(item)
3967     {
3968         var prev = false;
3969         Roo.each(this.navItems, function(v){
3970             if (v == item) {
3971                 return ;
3972             }
3973             if (v.isActive()) {
3974                 v.setActive(false, true);
3975                 prev = v;
3976                 
3977             }
3978             
3979         });
3980
3981         item.setActive(true, true);
3982         this.fireEvent('changed', this, item, prev);
3983         
3984         
3985     },
3986     /**
3987     * gets the active Navigation item
3988     * @return {Roo.bootstrap.NavItem} the current navitem
3989     */
3990     getActive : function()
3991     {
3992         
3993         var prev = false;
3994         Roo.each(this.navItems, function(v){
3995             
3996             if (v.isActive()) {
3997                 prev = v;
3998                 
3999             }
4000             
4001         });
4002         return prev;
4003     },
4004     
4005     indexOfNav : function()
4006     {
4007         
4008         var prev = false;
4009         Roo.each(this.navItems, function(v,i){
4010             
4011             if (v.isActive()) {
4012                 prev = i;
4013                 
4014             }
4015             
4016         });
4017         return prev;
4018     },
4019     /**
4020     * adds a Navigation item
4021     * @param {Roo.bootstrap.NavItem} the navitem to add
4022     */
4023     addItem : function(cfg)
4024     {
4025         var cn = new Roo.bootstrap.NavItem(cfg);
4026         this.register(cn);
4027         cn.parentId = this.id;
4028         cn.onRender(this.el, null);
4029         return cn;
4030     },
4031     /**
4032     * register a Navigation item
4033     * @param {Roo.bootstrap.NavItem} the navitem to add
4034     */
4035     register : function(item)
4036     {
4037         this.navItems.push( item);
4038         item.navId = this.navId;
4039     
4040     },
4041     
4042     /**
4043     * clear all the Navigation item
4044     */
4045    
4046     clearAll : function()
4047     {
4048         this.navItems = [];
4049         this.el.dom.innerHTML = '';
4050     },
4051     
4052     getNavItem: function(tabId)
4053     {
4054         var ret = false;
4055         Roo.each(this.navItems, function(e) {
4056             if (e.tabId == tabId) {
4057                ret =  e;
4058                return false;
4059             }
4060             return true;
4061             
4062         });
4063         return ret;
4064     },
4065     
4066     setActiveNext : function()
4067     {
4068         var i = this.indexOfNav(this.getActive());
4069         if (i > this.navItems.length) {
4070             return;
4071         }
4072         this.setActiveItem(this.navItems[i+1]);
4073     },
4074     setActivePrev : function()
4075     {
4076         var i = this.indexOfNav(this.getActive());
4077         if (i  < 1) {
4078             return;
4079         }
4080         this.setActiveItem(this.navItems[i-1]);
4081     },
4082     clearWasActive : function(except) {
4083         Roo.each(this.navItems, function(e) {
4084             if (e.tabId != except.tabId && e.was_active) {
4085                e.was_active = false;
4086                return false;
4087             }
4088             return true;
4089             
4090         });
4091     },
4092     getWasActive : function ()
4093     {
4094         var r = false;
4095         Roo.each(this.navItems, function(e) {
4096             if (e.was_active) {
4097                r = e;
4098                return false;
4099             }
4100             return true;
4101             
4102         });
4103         return r;
4104     }
4105     
4106     
4107 });
4108
4109  
4110 Roo.apply(Roo.bootstrap.NavGroup, {
4111     
4112     groups: {},
4113      /**
4114     * register a Navigation Group
4115     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4116     */
4117     register : function(navgrp)
4118     {
4119         this.groups[navgrp.navId] = navgrp;
4120         
4121     },
4122     /**
4123     * fetch a Navigation Group based on the navigation ID
4124     * @param {string} the navgroup to add
4125     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4126     */
4127     get: function(navId) {
4128         if (typeof(this.groups[navId]) == 'undefined') {
4129             return false;
4130             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4131         }
4132         return this.groups[navId] ;
4133     }
4134     
4135     
4136     
4137 });
4138
4139  /*
4140  * - LGPL
4141  *
4142  * row
4143  * 
4144  */
4145
4146 /**
4147  * @class Roo.bootstrap.NavItem
4148  * @extends Roo.bootstrap.Component
4149  * Bootstrap Navbar.NavItem class
4150  * @cfg {String} href  link to
4151  * @cfg {String} html content of button
4152  * @cfg {String} badge text inside badge
4153  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4154  * @cfg {String} glyphicon name of glyphicon
4155  * @cfg {String} icon name of font awesome icon
4156  * @cfg {Boolean} active Is item active
4157  * @cfg {Boolean} disabled Is item disabled
4158  
4159  * @cfg {Boolean} preventDefault (true | false) default false
4160  * @cfg {String} tabId the tab that this item activates.
4161  * @cfg {String} tagtype (a|span) render as a href or span?
4162  * @cfg {Boolean} animateRef (true|false) link to element default false  
4163   
4164  * @constructor
4165  * Create a new Navbar Item
4166  * @param {Object} config The config object
4167  */
4168 Roo.bootstrap.NavItem = function(config){
4169     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4170     this.addEvents({
4171         // raw events
4172         /**
4173          * @event click
4174          * The raw click event for the entire grid.
4175          * @param {Roo.EventObject} e
4176          */
4177         "click" : true,
4178          /**
4179             * @event changed
4180             * Fires when the active item active state changes
4181             * @param {Roo.bootstrap.NavItem} this
4182             * @param {boolean} state the new state
4183              
4184          */
4185         'changed': true,
4186         /**
4187             * @event scrollto
4188             * Fires when scroll to element
4189             * @param {Roo.bootstrap.NavItem} this
4190             * @param {Object} options
4191             * @param {Roo.EventObject} e
4192              
4193          */
4194         'scrollto': true
4195     });
4196    
4197 };
4198
4199 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4200     
4201     href: false,
4202     html: '',
4203     badge: '',
4204     icon: false,
4205     glyphicon: false,
4206     active: false,
4207     preventDefault : false,
4208     tabId : false,
4209     tagtype : 'a',
4210     disabled : false,
4211     animateRef : false,
4212     was_active : false,
4213     
4214     getAutoCreate : function(){
4215          
4216         var cfg = {
4217             tag: 'li',
4218             cls: 'nav-item'
4219             
4220         };
4221         
4222         if (this.active) {
4223             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4224         }
4225         if (this.disabled) {
4226             cfg.cls += ' disabled';
4227         }
4228         
4229         if (this.href || this.html || this.glyphicon || this.icon) {
4230             cfg.cn = [
4231                 {
4232                     tag: this.tagtype,
4233                     href : this.href || "#",
4234                     html: this.html || ''
4235                 }
4236             ];
4237             
4238             if (this.icon) {
4239                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4240             }
4241
4242             if(this.glyphicon) {
4243                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4244             }
4245             
4246             if (this.menu) {
4247                 
4248                 cfg.cn[0].html += " <span class='caret'></span>";
4249              
4250             }
4251             
4252             if (this.badge !== '') {
4253                  
4254                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4255             }
4256         }
4257         
4258         
4259         
4260         return cfg;
4261     },
4262     initEvents: function() 
4263     {
4264         if (typeof (this.menu) != 'undefined') {
4265             this.menu.parentType = this.xtype;
4266             this.menu.triggerEl = this.el;
4267             this.menu = this.addxtype(Roo.apply({}, this.menu));
4268         }
4269         
4270         this.el.select('a',true).on('click', this.onClick, this);
4271         
4272         if(this.tagtype == 'span'){
4273             this.el.select('span',true).on('click', this.onClick, this);
4274         }
4275        
4276         // at this point parent should be available..
4277         this.parent().register(this);
4278     },
4279     
4280     onClick : function(e)
4281     {
4282         if (e.getTarget('.dropdown-menu-item')) {
4283             // did you click on a menu itemm.... - then don't trigger onclick..
4284             return;
4285         }
4286         
4287         if(
4288                 this.preventDefault || 
4289                 this.href == '#' 
4290         ){
4291             Roo.log("NavItem - prevent Default?");
4292             e.preventDefault();
4293         }
4294         
4295         if (this.disabled) {
4296             return;
4297         }
4298         
4299         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4300         if (tg && tg.transition) {
4301             Roo.log("waiting for the transitionend");
4302             return;
4303         }
4304         
4305         
4306         
4307         //Roo.log("fire event clicked");
4308         if(this.fireEvent('click', this, e) === false){
4309             return;
4310         };
4311         
4312         if(this.tagtype == 'span'){
4313             return;
4314         }
4315         
4316         //Roo.log(this.href);
4317         var ael = this.el.select('a',true).first();
4318         //Roo.log(ael);
4319         
4320         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4321             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4322             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4323                 return; // ignore... - it's a 'hash' to another page.
4324             }
4325             Roo.log("NavItem - prevent Default?");
4326             e.preventDefault();
4327             this.scrollToElement(e);
4328         }
4329         
4330         
4331         var p =  this.parent();
4332    
4333         if (['tabs','pills'].indexOf(p.type)!==-1) {
4334             if (typeof(p.setActiveItem) !== 'undefined') {
4335                 p.setActiveItem(this);
4336             }
4337         }
4338         
4339         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4340         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4341             // remove the collapsed menu expand...
4342             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4343         }
4344     },
4345     
4346     isActive: function () {
4347         return this.active
4348     },
4349     setActive : function(state, fire, is_was_active)
4350     {
4351         if (this.active && !state && this.navId) {
4352             this.was_active = true;
4353             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4354             if (nv) {
4355                 nv.clearWasActive(this);
4356             }
4357             
4358         }
4359         this.active = state;
4360         
4361         if (!state ) {
4362             this.el.removeClass('active');
4363         } else if (!this.el.hasClass('active')) {
4364             this.el.addClass('active');
4365         }
4366         if (fire) {
4367             this.fireEvent('changed', this, state);
4368         }
4369         
4370         // show a panel if it's registered and related..
4371         
4372         if (!this.navId || !this.tabId || !state || is_was_active) {
4373             return;
4374         }
4375         
4376         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4377         if (!tg) {
4378             return;
4379         }
4380         var pan = tg.getPanelByName(this.tabId);
4381         if (!pan) {
4382             return;
4383         }
4384         // if we can not flip to new panel - go back to old nav highlight..
4385         if (false == tg.showPanel(pan)) {
4386             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4387             if (nv) {
4388                 var onav = nv.getWasActive();
4389                 if (onav) {
4390                     onav.setActive(true, false, true);
4391                 }
4392             }
4393             
4394         }
4395         
4396         
4397         
4398     },
4399      // this should not be here...
4400     setDisabled : function(state)
4401     {
4402         this.disabled = state;
4403         if (!state ) {
4404             this.el.removeClass('disabled');
4405         } else if (!this.el.hasClass('disabled')) {
4406             this.el.addClass('disabled');
4407         }
4408         
4409     },
4410     
4411     /**
4412      * Fetch the element to display the tooltip on.
4413      * @return {Roo.Element} defaults to this.el
4414      */
4415     tooltipEl : function()
4416     {
4417         return this.el.select('' + this.tagtype + '', true).first();
4418     },
4419     
4420     scrollToElement : function(e)
4421     {
4422         var c = document.body;
4423         
4424         /*
4425          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4426          */
4427         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4428             c = document.documentElement;
4429         }
4430         
4431         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4432         
4433         if(!target){
4434             return;
4435         }
4436
4437         var o = target.calcOffsetsTo(c);
4438         
4439         var options = {
4440             target : target,
4441             value : o[1]
4442         };
4443         
4444         this.fireEvent('scrollto', this, options, e);
4445         
4446         Roo.get(c).scrollTo('top', options.value, true);
4447         
4448         return;
4449     }
4450 });
4451  
4452
4453  /*
4454  * - LGPL
4455  *
4456  * sidebar item
4457  *
4458  *  li
4459  *    <span> icon </span>
4460  *    <span> text </span>
4461  *    <span>badge </span>
4462  */
4463
4464 /**
4465  * @class Roo.bootstrap.NavSidebarItem
4466  * @extends Roo.bootstrap.NavItem
4467  * Bootstrap Navbar.NavSidebarItem class
4468  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4469  * {bool} open is the menu open
4470  * @constructor
4471  * Create a new Navbar Button
4472  * @param {Object} config The config object
4473  */
4474 Roo.bootstrap.NavSidebarItem = function(config){
4475     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4476     this.addEvents({
4477         // raw events
4478         /**
4479          * @event click
4480          * The raw click event for the entire grid.
4481          * @param {Roo.EventObject} e
4482          */
4483         "click" : true,
4484          /**
4485             * @event changed
4486             * Fires when the active item active state changes
4487             * @param {Roo.bootstrap.NavSidebarItem} this
4488             * @param {boolean} state the new state
4489              
4490          */
4491         'changed': true
4492     });
4493    
4494 };
4495
4496 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4497     
4498     badgeWeight : 'default',
4499     
4500     open: false,
4501     
4502     getAutoCreate : function(){
4503         
4504         
4505         var a = {
4506                 tag: 'a',
4507                 href : this.href || '#',
4508                 cls: '',
4509                 html : '',
4510                 cn : []
4511         };
4512         var cfg = {
4513             tag: 'li',
4514             cls: '',
4515             cn: [ a ]
4516         };
4517         var span = {
4518             tag: 'span',
4519             html : this.html || ''
4520         };
4521         
4522         
4523         if (this.active) {
4524             cfg.cls += ' active';
4525         }
4526         
4527         if (this.disabled) {
4528             cfg.cls += ' disabled';
4529         }
4530         if (this.open) {
4531             cfg.cls += ' open x-open';
4532         }
4533         // left icon..
4534         if (this.glyphicon || this.icon) {
4535             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4536             a.cn.push({ tag : 'i', cls : c }) ;
4537         }
4538         // html..
4539         a.cn.push(span);
4540         // then badge..
4541         if (this.badge !== '') {
4542             
4543             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4544         }
4545         // fi
4546         if (this.menu) {
4547             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4548             a.cls += 'dropdown-toggle treeview' ;
4549         }
4550         
4551         return cfg;
4552          
4553            
4554     },
4555     
4556     initEvents : function()
4557     { 
4558         if (typeof (this.menu) != 'undefined') {
4559             this.menu.parentType = this.xtype;
4560             this.menu.triggerEl = this.el;
4561             this.menu = this.addxtype(Roo.apply({}, this.menu));
4562         }
4563         
4564         this.el.on('click', this.onClick, this);
4565        
4566     
4567         if(this.badge !== ''){
4568  
4569             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4570         }
4571         
4572     },
4573     
4574     onClick : function(e)
4575     {
4576         if(this.disabled){
4577             e.preventDefault();
4578             return;
4579         }
4580         
4581         if(this.preventDefault){
4582             e.preventDefault();
4583         }
4584         
4585         this.fireEvent('click', this);
4586     },
4587     
4588     disable : function()
4589     {
4590         this.setDisabled(true);
4591     },
4592     
4593     enable : function()
4594     {
4595         this.setDisabled(false);
4596     },
4597     
4598     setDisabled : function(state)
4599     {
4600         if(this.disabled == state){
4601             return;
4602         }
4603         
4604         this.disabled = state;
4605         
4606         if (state) {
4607             this.el.addClass('disabled');
4608             return;
4609         }
4610         
4611         this.el.removeClass('disabled');
4612         
4613         return;
4614     },
4615     
4616     setActive : function(state)
4617     {
4618         if(this.active == state){
4619             return;
4620         }
4621         
4622         this.active = state;
4623         
4624         if (state) {
4625             this.el.addClass('active');
4626             return;
4627         }
4628         
4629         this.el.removeClass('active');
4630         
4631         return;
4632     },
4633     
4634     isActive: function () 
4635     {
4636         return this.active;
4637     },
4638     
4639     setBadge : function(str)
4640     {
4641         if(!this.badgeEl){
4642             return;
4643         }
4644         
4645         this.badgeEl.dom.innerHTML = str;
4646     }
4647     
4648    
4649      
4650  
4651 });
4652  
4653
4654  /*
4655  * - LGPL
4656  *
4657  * row
4658  * 
4659  */
4660
4661 /**
4662  * @class Roo.bootstrap.Row
4663  * @extends Roo.bootstrap.Component
4664  * Bootstrap Row class (contains columns...)
4665  * 
4666  * @constructor
4667  * Create a new Row
4668  * @param {Object} config The config object
4669  */
4670
4671 Roo.bootstrap.Row = function(config){
4672     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4673 };
4674
4675 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4676     
4677     getAutoCreate : function(){
4678        return {
4679             cls: 'row clearfix'
4680        };
4681     }
4682     
4683     
4684 });
4685
4686  
4687
4688  /*
4689  * - LGPL
4690  *
4691  * element
4692  * 
4693  */
4694
4695 /**
4696  * @class Roo.bootstrap.Element
4697  * @extends Roo.bootstrap.Component
4698  * Bootstrap Element class
4699  * @cfg {String} html contents of the element
4700  * @cfg {String} tag tag of the element
4701  * @cfg {String} cls class of the element
4702  * @cfg {Boolean} preventDefault (true|false) default false
4703  * @cfg {Boolean} clickable (true|false) default false
4704  * 
4705  * @constructor
4706  * Create a new Element
4707  * @param {Object} config The config object
4708  */
4709
4710 Roo.bootstrap.Element = function(config){
4711     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4712     
4713     this.addEvents({
4714         // raw events
4715         /**
4716          * @event click
4717          * When a element is chick
4718          * @param {Roo.bootstrap.Element} this
4719          * @param {Roo.EventObject} e
4720          */
4721         "click" : true
4722     });
4723 };
4724
4725 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4726     
4727     tag: 'div',
4728     cls: '',
4729     html: '',
4730     preventDefault: false, 
4731     clickable: false,
4732     
4733     getAutoCreate : function(){
4734         
4735         var cfg = {
4736             tag: this.tag,
4737             cls: this.cls,
4738             html: this.html
4739         };
4740         
4741         return cfg;
4742     },
4743     
4744     initEvents: function() 
4745     {
4746         Roo.bootstrap.Element.superclass.initEvents.call(this);
4747         
4748         if(this.clickable){
4749             this.el.on('click', this.onClick, this);
4750         }
4751         
4752     },
4753     
4754     onClick : function(e)
4755     {
4756         if(this.preventDefault){
4757             e.preventDefault();
4758         }
4759         
4760         this.fireEvent('click', this, e);
4761     },
4762     
4763     getValue : function()
4764     {
4765         return this.el.dom.innerHTML;
4766     },
4767     
4768     setValue : function(value)
4769     {
4770         this.el.dom.innerHTML = value;
4771     }
4772    
4773 });
4774
4775  
4776
4777  /*
4778  * - LGPL
4779  *
4780  * pagination
4781  * 
4782  */
4783
4784 /**
4785  * @class Roo.bootstrap.Pagination
4786  * @extends Roo.bootstrap.Component
4787  * Bootstrap Pagination class
4788  * @cfg {String} size xs | sm | md | lg
4789  * @cfg {Boolean} inverse false | true
4790  * 
4791  * @constructor
4792  * Create a new Pagination
4793  * @param {Object} config The config object
4794  */
4795
4796 Roo.bootstrap.Pagination = function(config){
4797     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4798 };
4799
4800 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4801     
4802     cls: false,
4803     size: false,
4804     inverse: false,
4805     
4806     getAutoCreate : function(){
4807         var cfg = {
4808             tag: 'ul',
4809                 cls: 'pagination'
4810         };
4811         if (this.inverse) {
4812             cfg.cls += ' inverse';
4813         }
4814         if (this.html) {
4815             cfg.html=this.html;
4816         }
4817         if (this.cls) {
4818             cfg.cls += " " + this.cls;
4819         }
4820         return cfg;
4821     }
4822    
4823 });
4824
4825  
4826
4827  /*
4828  * - LGPL
4829  *
4830  * Pagination item
4831  * 
4832  */
4833
4834
4835 /**
4836  * @class Roo.bootstrap.PaginationItem
4837  * @extends Roo.bootstrap.Component
4838  * Bootstrap PaginationItem class
4839  * @cfg {String} html text
4840  * @cfg {String} href the link
4841  * @cfg {Boolean} preventDefault (true | false) default true
4842  * @cfg {Boolean} active (true | false) default false
4843  * @cfg {Boolean} disabled default false
4844  * 
4845  * 
4846  * @constructor
4847  * Create a new PaginationItem
4848  * @param {Object} config The config object
4849  */
4850
4851
4852 Roo.bootstrap.PaginationItem = function(config){
4853     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4854     this.addEvents({
4855         // raw events
4856         /**
4857          * @event click
4858          * The raw click event for the entire grid.
4859          * @param {Roo.EventObject} e
4860          */
4861         "click" : true
4862     });
4863 };
4864
4865 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4866     
4867     href : false,
4868     html : false,
4869     preventDefault: true,
4870     active : false,
4871     cls : false,
4872     disabled: false,
4873     
4874     getAutoCreate : function(){
4875         var cfg= {
4876             tag: 'li',
4877             cn: [
4878                 {
4879                     tag : 'a',
4880                     href : this.href ? this.href : '#',
4881                     html : this.html ? this.html : ''
4882                 }
4883             ]
4884         };
4885         
4886         if(this.cls){
4887             cfg.cls = this.cls;
4888         }
4889         
4890         if(this.disabled){
4891             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4892         }
4893         
4894         if(this.active){
4895             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4896         }
4897         
4898         return cfg;
4899     },
4900     
4901     initEvents: function() {
4902         
4903         this.el.on('click', this.onClick, this);
4904         
4905     },
4906     onClick : function(e)
4907     {
4908         Roo.log('PaginationItem on click ');
4909         if(this.preventDefault){
4910             e.preventDefault();
4911         }
4912         
4913         if(this.disabled){
4914             return;
4915         }
4916         
4917         this.fireEvent('click', this, e);
4918     }
4919    
4920 });
4921
4922  
4923
4924  /*
4925  * - LGPL
4926  *
4927  * slider
4928  * 
4929  */
4930
4931
4932 /**
4933  * @class Roo.bootstrap.Slider
4934  * @extends Roo.bootstrap.Component
4935  * Bootstrap Slider class
4936  *    
4937  * @constructor
4938  * Create a new Slider
4939  * @param {Object} config The config object
4940  */
4941
4942 Roo.bootstrap.Slider = function(config){
4943     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4944 };
4945
4946 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4947     
4948     getAutoCreate : function(){
4949         
4950         var cfg = {
4951             tag: 'div',
4952             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4953             cn: [
4954                 {
4955                     tag: 'a',
4956                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4957                 }
4958             ]
4959         };
4960         
4961         return cfg;
4962     }
4963    
4964 });
4965
4966  /*
4967  * Based on:
4968  * Ext JS Library 1.1.1
4969  * Copyright(c) 2006-2007, Ext JS, LLC.
4970  *
4971  * Originally Released Under LGPL - original licence link has changed is not relivant.
4972  *
4973  * Fork - LGPL
4974  * <script type="text/javascript">
4975  */
4976  
4977
4978 /**
4979  * @class Roo.grid.ColumnModel
4980  * @extends Roo.util.Observable
4981  * This is the default implementation of a ColumnModel used by the Grid. It defines
4982  * the columns in the grid.
4983  * <br>Usage:<br>
4984  <pre><code>
4985  var colModel = new Roo.grid.ColumnModel([
4986         {header: "Ticker", width: 60, sortable: true, locked: true},
4987         {header: "Company Name", width: 150, sortable: true},
4988         {header: "Market Cap.", width: 100, sortable: true},
4989         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4990         {header: "Employees", width: 100, sortable: true, resizable: false}
4991  ]);
4992  </code></pre>
4993  * <p>
4994  
4995  * The config options listed for this class are options which may appear in each
4996  * individual column definition.
4997  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4998  * @constructor
4999  * @param {Object} config An Array of column config objects. See this class's
5000  * config objects for details.
5001 */
5002 Roo.grid.ColumnModel = function(config){
5003         /**
5004      * The config passed into the constructor
5005      */
5006     this.config = config;
5007     this.lookup = {};
5008
5009     // if no id, create one
5010     // if the column does not have a dataIndex mapping,
5011     // map it to the order it is in the config
5012     for(var i = 0, len = config.length; i < len; i++){
5013         var c = config[i];
5014         if(typeof c.dataIndex == "undefined"){
5015             c.dataIndex = i;
5016         }
5017         if(typeof c.renderer == "string"){
5018             c.renderer = Roo.util.Format[c.renderer];
5019         }
5020         if(typeof c.id == "undefined"){
5021             c.id = Roo.id();
5022         }
5023         if(c.editor && c.editor.xtype){
5024             c.editor  = Roo.factory(c.editor, Roo.grid);
5025         }
5026         if(c.editor && c.editor.isFormField){
5027             c.editor = new Roo.grid.GridEditor(c.editor);
5028         }
5029         this.lookup[c.id] = c;
5030     }
5031
5032     /**
5033      * The width of columns which have no width specified (defaults to 100)
5034      * @type Number
5035      */
5036     this.defaultWidth = 100;
5037
5038     /**
5039      * Default sortable of columns which have no sortable specified (defaults to false)
5040      * @type Boolean
5041      */
5042     this.defaultSortable = false;
5043
5044     this.addEvents({
5045         /**
5046              * @event widthchange
5047              * Fires when the width of a column changes.
5048              * @param {ColumnModel} this
5049              * @param {Number} columnIndex The column index
5050              * @param {Number} newWidth The new width
5051              */
5052             "widthchange": true,
5053         /**
5054              * @event headerchange
5055              * Fires when the text of a header changes.
5056              * @param {ColumnModel} this
5057              * @param {Number} columnIndex The column index
5058              * @param {Number} newText The new header text
5059              */
5060             "headerchange": true,
5061         /**
5062              * @event hiddenchange
5063              * Fires when a column is hidden or "unhidden".
5064              * @param {ColumnModel} this
5065              * @param {Number} columnIndex The column index
5066              * @param {Boolean} hidden true if hidden, false otherwise
5067              */
5068             "hiddenchange": true,
5069             /**
5070          * @event columnmoved
5071          * Fires when a column is moved.
5072          * @param {ColumnModel} this
5073          * @param {Number} oldIndex
5074          * @param {Number} newIndex
5075          */
5076         "columnmoved" : true,
5077         /**
5078          * @event columlockchange
5079          * Fires when a column's locked state is changed
5080          * @param {ColumnModel} this
5081          * @param {Number} colIndex
5082          * @param {Boolean} locked true if locked
5083          */
5084         "columnlockchange" : true
5085     });
5086     Roo.grid.ColumnModel.superclass.constructor.call(this);
5087 };
5088 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5089     /**
5090      * @cfg {String} header The header text to display in the Grid view.
5091      */
5092     /**
5093      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5094      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5095      * specified, the column's index is used as an index into the Record's data Array.
5096      */
5097     /**
5098      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5099      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5100      */
5101     /**
5102      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5103      * Defaults to the value of the {@link #defaultSortable} property.
5104      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5105      */
5106     /**
5107      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5108      */
5109     /**
5110      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5111      */
5112     /**
5113      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5114      */
5115     /**
5116      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5117      */
5118     /**
5119      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5120      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5121      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5122      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5123      */
5124        /**
5125      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5126      */
5127     /**
5128      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5129      */
5130     /**
5131      * @cfg {String} cursor (Optional)
5132      */
5133     /**
5134      * @cfg {String} tooltip (Optional)
5135      */
5136     /**
5137      * @cfg {Number} xs (Optional)
5138      */
5139     /**
5140      * @cfg {Number} sm (Optional)
5141      */
5142     /**
5143      * @cfg {Number} md (Optional)
5144      */
5145     /**
5146      * @cfg {Number} lg (Optional)
5147      */
5148     /**
5149      * Returns the id of the column at the specified index.
5150      * @param {Number} index The column index
5151      * @return {String} the id
5152      */
5153     getColumnId : function(index){
5154         return this.config[index].id;
5155     },
5156
5157     /**
5158      * Returns the column for a specified id.
5159      * @param {String} id The column id
5160      * @return {Object} the column
5161      */
5162     getColumnById : function(id){
5163         return this.lookup[id];
5164     },
5165
5166     
5167     /**
5168      * Returns the column for a specified dataIndex.
5169      * @param {String} dataIndex The column dataIndex
5170      * @return {Object|Boolean} the column or false if not found
5171      */
5172     getColumnByDataIndex: function(dataIndex){
5173         var index = this.findColumnIndex(dataIndex);
5174         return index > -1 ? this.config[index] : false;
5175     },
5176     
5177     /**
5178      * Returns the index for a specified column id.
5179      * @param {String} id The column id
5180      * @return {Number} the index, or -1 if not found
5181      */
5182     getIndexById : function(id){
5183         for(var i = 0, len = this.config.length; i < len; i++){
5184             if(this.config[i].id == id){
5185                 return i;
5186             }
5187         }
5188         return -1;
5189     },
5190     
5191     /**
5192      * Returns the index for a specified column dataIndex.
5193      * @param {String} dataIndex The column dataIndex
5194      * @return {Number} the index, or -1 if not found
5195      */
5196     
5197     findColumnIndex : function(dataIndex){
5198         for(var i = 0, len = this.config.length; i < len; i++){
5199             if(this.config[i].dataIndex == dataIndex){
5200                 return i;
5201             }
5202         }
5203         return -1;
5204     },
5205     
5206     
5207     moveColumn : function(oldIndex, newIndex){
5208         var c = this.config[oldIndex];
5209         this.config.splice(oldIndex, 1);
5210         this.config.splice(newIndex, 0, c);
5211         this.dataMap = null;
5212         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5213     },
5214
5215     isLocked : function(colIndex){
5216         return this.config[colIndex].locked === true;
5217     },
5218
5219     setLocked : function(colIndex, value, suppressEvent){
5220         if(this.isLocked(colIndex) == value){
5221             return;
5222         }
5223         this.config[colIndex].locked = value;
5224         if(!suppressEvent){
5225             this.fireEvent("columnlockchange", this, colIndex, value);
5226         }
5227     },
5228
5229     getTotalLockedWidth : function(){
5230         var totalWidth = 0;
5231         for(var i = 0; i < this.config.length; i++){
5232             if(this.isLocked(i) && !this.isHidden(i)){
5233                 this.totalWidth += this.getColumnWidth(i);
5234             }
5235         }
5236         return totalWidth;
5237     },
5238
5239     getLockedCount : function(){
5240         for(var i = 0, len = this.config.length; i < len; i++){
5241             if(!this.isLocked(i)){
5242                 return i;
5243             }
5244         }
5245         
5246         return this.config.length;
5247     },
5248
5249     /**
5250      * Returns the number of columns.
5251      * @return {Number}
5252      */
5253     getColumnCount : function(visibleOnly){
5254         if(visibleOnly === true){
5255             var c = 0;
5256             for(var i = 0, len = this.config.length; i < len; i++){
5257                 if(!this.isHidden(i)){
5258                     c++;
5259                 }
5260             }
5261             return c;
5262         }
5263         return this.config.length;
5264     },
5265
5266     /**
5267      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5268      * @param {Function} fn
5269      * @param {Object} scope (optional)
5270      * @return {Array} result
5271      */
5272     getColumnsBy : function(fn, scope){
5273         var r = [];
5274         for(var i = 0, len = this.config.length; i < len; i++){
5275             var c = this.config[i];
5276             if(fn.call(scope||this, c, i) === true){
5277                 r[r.length] = c;
5278             }
5279         }
5280         return r;
5281     },
5282
5283     /**
5284      * Returns true if the specified column is sortable.
5285      * @param {Number} col The column index
5286      * @return {Boolean}
5287      */
5288     isSortable : function(col){
5289         if(typeof this.config[col].sortable == "undefined"){
5290             return this.defaultSortable;
5291         }
5292         return this.config[col].sortable;
5293     },
5294
5295     /**
5296      * Returns the rendering (formatting) function defined for the column.
5297      * @param {Number} col The column index.
5298      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5299      */
5300     getRenderer : function(col){
5301         if(!this.config[col].renderer){
5302             return Roo.grid.ColumnModel.defaultRenderer;
5303         }
5304         return this.config[col].renderer;
5305     },
5306
5307     /**
5308      * Sets the rendering (formatting) function for a column.
5309      * @param {Number} col The column index
5310      * @param {Function} fn The function to use to process the cell's raw data
5311      * to return HTML markup for the grid view. The render function is called with
5312      * the following parameters:<ul>
5313      * <li>Data value.</li>
5314      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5315      * <li>css A CSS style string to apply to the table cell.</li>
5316      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5317      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5318      * <li>Row index</li>
5319      * <li>Column index</li>
5320      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5321      */
5322     setRenderer : function(col, fn){
5323         this.config[col].renderer = fn;
5324     },
5325
5326     /**
5327      * Returns the width for the specified column.
5328      * @param {Number} col The column index
5329      * @return {Number}
5330      */
5331     getColumnWidth : function(col){
5332         return this.config[col].width * 1 || this.defaultWidth;
5333     },
5334
5335     /**
5336      * Sets the width for a column.
5337      * @param {Number} col The column index
5338      * @param {Number} width The new width
5339      */
5340     setColumnWidth : function(col, width, suppressEvent){
5341         this.config[col].width = width;
5342         this.totalWidth = null;
5343         if(!suppressEvent){
5344              this.fireEvent("widthchange", this, col, width);
5345         }
5346     },
5347
5348     /**
5349      * Returns the total width of all columns.
5350      * @param {Boolean} includeHidden True to include hidden column widths
5351      * @return {Number}
5352      */
5353     getTotalWidth : function(includeHidden){
5354         if(!this.totalWidth){
5355             this.totalWidth = 0;
5356             for(var i = 0, len = this.config.length; i < len; i++){
5357                 if(includeHidden || !this.isHidden(i)){
5358                     this.totalWidth += this.getColumnWidth(i);
5359                 }
5360             }
5361         }
5362         return this.totalWidth;
5363     },
5364
5365     /**
5366      * Returns the header for the specified column.
5367      * @param {Number} col The column index
5368      * @return {String}
5369      */
5370     getColumnHeader : function(col){
5371         return this.config[col].header;
5372     },
5373
5374     /**
5375      * Sets the header for a column.
5376      * @param {Number} col The column index
5377      * @param {String} header The new header
5378      */
5379     setColumnHeader : function(col, header){
5380         this.config[col].header = header;
5381         this.fireEvent("headerchange", this, col, header);
5382     },
5383
5384     /**
5385      * Returns the tooltip for the specified column.
5386      * @param {Number} col The column index
5387      * @return {String}
5388      */
5389     getColumnTooltip : function(col){
5390             return this.config[col].tooltip;
5391     },
5392     /**
5393      * Sets the tooltip for a column.
5394      * @param {Number} col The column index
5395      * @param {String} tooltip The new tooltip
5396      */
5397     setColumnTooltip : function(col, tooltip){
5398             this.config[col].tooltip = tooltip;
5399     },
5400
5401     /**
5402      * Returns the dataIndex for the specified column.
5403      * @param {Number} col The column index
5404      * @return {Number}
5405      */
5406     getDataIndex : function(col){
5407         return this.config[col].dataIndex;
5408     },
5409
5410     /**
5411      * Sets the dataIndex for a column.
5412      * @param {Number} col The column index
5413      * @param {Number} dataIndex The new dataIndex
5414      */
5415     setDataIndex : function(col, dataIndex){
5416         this.config[col].dataIndex = dataIndex;
5417     },
5418
5419     
5420     
5421     /**
5422      * Returns true if the cell is editable.
5423      * @param {Number} colIndex The column index
5424      * @param {Number} rowIndex The row index - this is nto actually used..?
5425      * @return {Boolean}
5426      */
5427     isCellEditable : function(colIndex, rowIndex){
5428         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5429     },
5430
5431     /**
5432      * Returns the editor defined for the cell/column.
5433      * return false or null to disable editing.
5434      * @param {Number} colIndex The column index
5435      * @param {Number} rowIndex The row index
5436      * @return {Object}
5437      */
5438     getCellEditor : function(colIndex, rowIndex){
5439         return this.config[colIndex].editor;
5440     },
5441
5442     /**
5443      * Sets if a column is editable.
5444      * @param {Number} col The column index
5445      * @param {Boolean} editable True if the column is editable
5446      */
5447     setEditable : function(col, editable){
5448         this.config[col].editable = editable;
5449     },
5450
5451
5452     /**
5453      * Returns true if the column is hidden.
5454      * @param {Number} colIndex The column index
5455      * @return {Boolean}
5456      */
5457     isHidden : function(colIndex){
5458         return this.config[colIndex].hidden;
5459     },
5460
5461
5462     /**
5463      * Returns true if the column width cannot be changed
5464      */
5465     isFixed : function(colIndex){
5466         return this.config[colIndex].fixed;
5467     },
5468
5469     /**
5470      * Returns true if the column can be resized
5471      * @return {Boolean}
5472      */
5473     isResizable : function(colIndex){
5474         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5475     },
5476     /**
5477      * Sets if a column is hidden.
5478      * @param {Number} colIndex The column index
5479      * @param {Boolean} hidden True if the column is hidden
5480      */
5481     setHidden : function(colIndex, hidden){
5482         this.config[colIndex].hidden = hidden;
5483         this.totalWidth = null;
5484         this.fireEvent("hiddenchange", this, colIndex, hidden);
5485     },
5486
5487     /**
5488      * Sets the editor for a column.
5489      * @param {Number} col The column index
5490      * @param {Object} editor The editor object
5491      */
5492     setEditor : function(col, editor){
5493         this.config[col].editor = editor;
5494     }
5495 });
5496
5497 Roo.grid.ColumnModel.defaultRenderer = function(value){
5498         if(typeof value == "string" && value.length < 1){
5499             return "&#160;";
5500         }
5501         return value;
5502 };
5503
5504 // Alias for backwards compatibility
5505 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5506 /*
5507  * Based on:
5508  * Ext JS Library 1.1.1
5509  * Copyright(c) 2006-2007, Ext JS, LLC.
5510  *
5511  * Originally Released Under LGPL - original licence link has changed is not relivant.
5512  *
5513  * Fork - LGPL
5514  * <script type="text/javascript">
5515  */
5516  
5517 /**
5518  * @class Roo.LoadMask
5519  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5520  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5521  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5522  * element's UpdateManager load indicator and will be destroyed after the initial load.
5523  * @constructor
5524  * Create a new LoadMask
5525  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5526  * @param {Object} config The config object
5527  */
5528 Roo.LoadMask = function(el, config){
5529     this.el = Roo.get(el);
5530     Roo.apply(this, config);
5531     if(this.store){
5532         this.store.on('beforeload', this.onBeforeLoad, this);
5533         this.store.on('load', this.onLoad, this);
5534         this.store.on('loadexception', this.onLoadException, this);
5535         this.removeMask = false;
5536     }else{
5537         var um = this.el.getUpdateManager();
5538         um.showLoadIndicator = false; // disable the default indicator
5539         um.on('beforeupdate', this.onBeforeLoad, this);
5540         um.on('update', this.onLoad, this);
5541         um.on('failure', this.onLoad, this);
5542         this.removeMask = true;
5543     }
5544 };
5545
5546 Roo.LoadMask.prototype = {
5547     /**
5548      * @cfg {Boolean} removeMask
5549      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5550      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5551      */
5552     /**
5553      * @cfg {String} msg
5554      * The text to display in a centered loading message box (defaults to 'Loading...')
5555      */
5556     msg : 'Loading...',
5557     /**
5558      * @cfg {String} msgCls
5559      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5560      */
5561     msgCls : 'x-mask-loading',
5562
5563     /**
5564      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5565      * @type Boolean
5566      */
5567     disabled: false,
5568
5569     /**
5570      * Disables the mask to prevent it from being displayed
5571      */
5572     disable : function(){
5573        this.disabled = true;
5574     },
5575
5576     /**
5577      * Enables the mask so that it can be displayed
5578      */
5579     enable : function(){
5580         this.disabled = false;
5581     },
5582     
5583     onLoadException : function()
5584     {
5585         Roo.log(arguments);
5586         
5587         if (typeof(arguments[3]) != 'undefined') {
5588             Roo.MessageBox.alert("Error loading",arguments[3]);
5589         } 
5590         /*
5591         try {
5592             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5593                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5594             }   
5595         } catch(e) {
5596             
5597         }
5598         */
5599     
5600         
5601         
5602         this.el.unmask(this.removeMask);
5603     },
5604     // private
5605     onLoad : function()
5606     {
5607         this.el.unmask(this.removeMask);
5608     },
5609
5610     // private
5611     onBeforeLoad : function(){
5612         if(!this.disabled){
5613             this.el.mask(this.msg, this.msgCls);
5614         }
5615     },
5616
5617     // private
5618     destroy : function(){
5619         if(this.store){
5620             this.store.un('beforeload', this.onBeforeLoad, this);
5621             this.store.un('load', this.onLoad, this);
5622             this.store.un('loadexception', this.onLoadException, this);
5623         }else{
5624             var um = this.el.getUpdateManager();
5625             um.un('beforeupdate', this.onBeforeLoad, this);
5626             um.un('update', this.onLoad, this);
5627             um.un('failure', this.onLoad, this);
5628         }
5629     }
5630 };/*
5631  * - LGPL
5632  *
5633  * table
5634  * 
5635  */
5636
5637 /**
5638  * @class Roo.bootstrap.Table
5639  * @extends Roo.bootstrap.Component
5640  * Bootstrap Table class
5641  * @cfg {String} cls table class
5642  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5643  * @cfg {String} bgcolor Specifies the background color for a table
5644  * @cfg {Number} border Specifies whether the table cells should have borders or not
5645  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5646  * @cfg {Number} cellspacing Specifies the space between cells
5647  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5648  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5649  * @cfg {String} sortable Specifies that the table should be sortable
5650  * @cfg {String} summary Specifies a summary of the content of a table
5651  * @cfg {Number} width Specifies the width of a table
5652  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5653  * 
5654  * @cfg {boolean} striped Should the rows be alternative striped
5655  * @cfg {boolean} bordered Add borders to the table
5656  * @cfg {boolean} hover Add hover highlighting
5657  * @cfg {boolean} condensed Format condensed
5658  * @cfg {boolean} responsive Format condensed
5659  * @cfg {Boolean} loadMask (true|false) default false
5660  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5661  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5662  * @cfg {Boolean} rowSelection (true|false) default false
5663  * @cfg {Boolean} cellSelection (true|false) default false
5664  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5665  
5666  * 
5667  * @constructor
5668  * Create a new Table
5669  * @param {Object} config The config object
5670  */
5671
5672 Roo.bootstrap.Table = function(config){
5673     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5674     
5675     if (config.container) {
5676         // ctor'ed from a Border/panel.grid
5677         this.container = Roo.get(config.container);
5678         this.container.update("");
5679         this.container.setStyle("overflow", "hidden");
5680         this.container.addClass('x-grid-container');
5681
5682     }
5683     
5684     // BC...
5685     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5686     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5687     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5688     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5689     
5690     
5691     if (this.sm) {
5692         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5693         this.sm = this.selModel;
5694         this.sm.xmodule = this.xmodule || false;
5695     }
5696     if (this.cm && typeof(this.cm.config) == 'undefined') {
5697         this.colModel = new Roo.grid.ColumnModel(this.cm);
5698         this.cm = this.colModel;
5699         this.cm.xmodule = this.xmodule || false;
5700     }
5701     if (this.store) {
5702         this.store= Roo.factory(this.store, Roo.data);
5703         this.ds = this.store;
5704         this.ds.xmodule = this.xmodule || false;
5705          
5706     }
5707     if (this.footer && this.store) {
5708         this.footer.dataSource = this.ds;
5709         this.footer = Roo.factory(this.footer);
5710     }
5711     
5712     /** @private */
5713     this.addEvents({
5714         /**
5715          * @event cellclick
5716          * Fires when a cell is clicked
5717          * @param {Roo.bootstrap.Table} this
5718          * @param {Roo.Element} el
5719          * @param {Number} rowIndex
5720          * @param {Number} columnIndex
5721          * @param {Roo.EventObject} e
5722          */
5723         "cellclick" : true,
5724         /**
5725          * @event celldblclick
5726          * Fires when a cell is double clicked
5727          * @param {Roo.bootstrap.Table} this
5728          * @param {Roo.Element} el
5729          * @param {Number} rowIndex
5730          * @param {Number} columnIndex
5731          * @param {Roo.EventObject} e
5732          */
5733         "celldblclick" : true,
5734         /**
5735          * @event rowclick
5736          * Fires when a row is clicked
5737          * @param {Roo.bootstrap.Table} this
5738          * @param {Roo.Element} el
5739          * @param {Number} rowIndex
5740          * @param {Roo.EventObject} e
5741          */
5742         "rowclick" : true,
5743         /**
5744          * @event rowdblclick
5745          * Fires when a row is double clicked
5746          * @param {Roo.bootstrap.Table} this
5747          * @param {Roo.Element} el
5748          * @param {Number} rowIndex
5749          * @param {Roo.EventObject} e
5750          */
5751         "rowdblclick" : true,
5752         /**
5753          * @event mouseover
5754          * Fires when a mouseover occur
5755          * @param {Roo.bootstrap.Table} this
5756          * @param {Roo.Element} el
5757          * @param {Number} rowIndex
5758          * @param {Number} columnIndex
5759          * @param {Roo.EventObject} e
5760          */
5761         "mouseover" : true,
5762         /**
5763          * @event mouseout
5764          * Fires when a mouseout occur
5765          * @param {Roo.bootstrap.Table} this
5766          * @param {Roo.Element} el
5767          * @param {Number} rowIndex
5768          * @param {Number} columnIndex
5769          * @param {Roo.EventObject} e
5770          */
5771         "mouseout" : true,
5772         /**
5773          * @event rowclass
5774          * Fires when a row is rendered, so you can change add a style to it.
5775          * @param {Roo.bootstrap.Table} this
5776          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5777          */
5778         'rowclass' : true,
5779           /**
5780          * @event rowsrendered
5781          * Fires when all the  rows have been rendered
5782          * @param {Roo.bootstrap.Table} this
5783          */
5784         'rowsrendered' : true,
5785         /**
5786          * @event contextmenu
5787          * The raw contextmenu event for the entire grid.
5788          * @param {Roo.EventObject} e
5789          */
5790         "contextmenu" : true,
5791         /**
5792          * @event rowcontextmenu
5793          * Fires when a row is right clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Number} rowIndex
5796          * @param {Roo.EventObject} e
5797          */
5798         "rowcontextmenu" : true,
5799         /**
5800          * @event cellcontextmenu
5801          * Fires when a cell is right clicked
5802          * @param {Roo.bootstrap.Table} this
5803          * @param {Number} rowIndex
5804          * @param {Number} cellIndex
5805          * @param {Roo.EventObject} e
5806          */
5807          "cellcontextmenu" : true,
5808          /**
5809          * @event headercontextmenu
5810          * Fires when a header is right clicked
5811          * @param {Roo.bootstrap.Table} this
5812          * @param {Number} columnIndex
5813          * @param {Roo.EventObject} e
5814          */
5815         "headercontextmenu" : true
5816     });
5817 };
5818
5819 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5820     
5821     cls: false,
5822     align: false,
5823     bgcolor: false,
5824     border: false,
5825     cellpadding: false,
5826     cellspacing: false,
5827     frame: false,
5828     rules: false,
5829     sortable: false,
5830     summary: false,
5831     width: false,
5832     striped : false,
5833     bordered: false,
5834     hover:  false,
5835     condensed : false,
5836     responsive : false,
5837     sm : false,
5838     cm : false,
5839     store : false,
5840     loadMask : false,
5841     footerShow : true,
5842     headerShow : true,
5843   
5844     rowSelection : false,
5845     cellSelection : false,
5846     layout : false,
5847     
5848     // Roo.Element - the tbody
5849     mainBody: false,
5850     
5851     
5852     container: false, // used by gridpanel...
5853     
5854     getAutoCreate : function()
5855     {
5856         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5857         
5858         cfg = {
5859             tag: 'table',
5860             cls : 'table table-body-fixed',
5861             cn : []
5862         };
5863             
5864         if (this.striped) {
5865             cfg.cls += ' table-striped';
5866         }
5867         
5868         if (this.hover) {
5869             cfg.cls += ' table-hover';
5870         }
5871         if (this.bordered) {
5872             cfg.cls += ' table-bordered';
5873         }
5874         if (this.condensed) {
5875             cfg.cls += ' table-condensed';
5876         }
5877         if (this.responsive) {
5878             cfg.cls += ' table-responsive';
5879         }
5880         
5881         if (this.cls) {
5882             cfg.cls+=  ' ' +this.cls;
5883         }
5884         
5885         // this lot should be simplifed...
5886         
5887         if (this.align) {
5888             cfg.align=this.align;
5889         }
5890         if (this.bgcolor) {
5891             cfg.bgcolor=this.bgcolor;
5892         }
5893         if (this.border) {
5894             cfg.border=this.border;
5895         }
5896         if (this.cellpadding) {
5897             cfg.cellpadding=this.cellpadding;
5898         }
5899         if (this.cellspacing) {
5900             cfg.cellspacing=this.cellspacing;
5901         }
5902         if (this.frame) {
5903             cfg.frame=this.frame;
5904         }
5905         if (this.rules) {
5906             cfg.rules=this.rules;
5907         }
5908         if (this.sortable) {
5909             cfg.sortable=this.sortable;
5910         }
5911         if (this.summary) {
5912             cfg.summary=this.summary;
5913         }
5914         if (this.width) {
5915             cfg.width=this.width;
5916         }
5917         if (this.layout) {
5918             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5919         }
5920         
5921         if(this.store || this.cm){
5922             if(this.headerShow){
5923                 cfg.cn.push(this.renderHeader());
5924             }
5925             
5926             cfg.cn.push(this.renderBody());
5927             
5928             if(this.footerShow){
5929                 cfg.cn.push(this.renderFooter());
5930             }
5931             // where does this come from?
5932             //cfg.cls+=  ' TableGrid';
5933         }
5934         
5935         return { cn : [ cfg ] };
5936     },
5937     
5938     initEvents : function()
5939     {   
5940         if(!this.store || !this.cm){
5941             return;
5942         }
5943         
5944         //Roo.log('initEvents with ds!!!!');
5945         
5946         this.mainBody = this.el.select('tbody', true).first();
5947         
5948         
5949         var _this = this;
5950         
5951         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5952             e.on('click', _this.sort, _this);
5953         });
5954         
5955         this.el.on("click", this.onClick, this);
5956         this.el.on("dblclick", this.onDblClick, this);
5957         
5958         // why is this done????? = it breaks dialogs??
5959         //this.parent().el.setStyle('position', 'relative');
5960         
5961         
5962         if (this.footer) {
5963             this.footer.parentId = this.id;
5964             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5965         }
5966         
5967         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5968         
5969         this.store.on('load', this.onLoad, this);
5970         this.store.on('beforeload', this.onBeforeLoad, this);
5971         this.store.on('update', this.onUpdate, this);
5972         this.store.on('add', this.onAdd, this);
5973         
5974         this.el.on("contextmenu", this.onContextMenu, this);
5975         
5976         
5977         
5978     },
5979     
5980     onContextMenu : function(e, t)
5981     {
5982         this.processEvent("contextmenu", e);
5983     },
5984     
5985     processEvent : function(name, e)
5986     {
5987         if (name != 'touchstart' ) {
5988             this.fireEvent(name, e);    
5989         }
5990         
5991         var t = e.getTarget();
5992         
5993         var cell = Roo.get(t);
5994         
5995         if(!cell){
5996             return;
5997         }
5998         
5999         if(cell.findParent('tfoot', false, true)){
6000             return;
6001         }
6002         
6003         if(cell.findParent('thead', false, true)){
6004             
6005             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6006                 cell = Roo.get(t).findParent('th', false, true);
6007                 if (!cell) {
6008                     Roo.log("failed to find th in thead?");
6009                     Roo.log(e.getTarget());
6010                     return;
6011                 }
6012             }
6013             
6014             var cellIndex = cell.dom.cellIndex;
6015             
6016             var ename = name == 'touchstart' ? 'click' : name;
6017             this.fireEvent("header" + ename, this, cellIndex, e);
6018             
6019             return;
6020         }
6021         
6022         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6023             cell = Roo.get(t).findParent('td', false, true);
6024         }
6025         
6026         var row = cell.findParent('tr', false, true);
6027         var cellIndex = cell.dom.cellIndex;
6028         var rowIndex = row.dom.rowIndex - 1;
6029         
6030         if(row !== false){
6031             
6032             this.fireEvent("row" + name, this, rowIndex, e);
6033             
6034             if(cell !== false){
6035             
6036                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6037             }
6038         }
6039         
6040     },
6041     
6042     onMouseover : function(e, el)
6043     {
6044         var cell = Roo.get(el);
6045         
6046         if(!cell){
6047             return;
6048         }
6049         
6050         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6051             cell = cell.findParent('td', false, true);
6052         }
6053         
6054         var row = cell.findParent('tr', false, true);
6055         var cellIndex = cell.dom.cellIndex;
6056         var rowIndex = row.dom.rowIndex - 1; // start from 0
6057         
6058         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6059         
6060     },
6061     
6062     onMouseout : function(e, el)
6063     {
6064         var cell = Roo.get(el);
6065         
6066         if(!cell){
6067             return;
6068         }
6069         
6070         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6071             cell = cell.findParent('td', false, true);
6072         }
6073         
6074         var row = cell.findParent('tr', false, true);
6075         var cellIndex = cell.dom.cellIndex;
6076         var rowIndex = row.dom.rowIndex - 1; // start from 0
6077         
6078         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6079         
6080     },
6081     
6082     onClick : function(e, el)
6083     {
6084         var cell = Roo.get(el);
6085         
6086         if(!cell || (!this.cellSelection && !this.rowSelection)){
6087             return;
6088         }
6089         
6090         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6091             cell = cell.findParent('td', false, true);
6092         }
6093         
6094         if(!cell || typeof(cell) == 'undefined'){
6095             return;
6096         }
6097         
6098         var row = cell.findParent('tr', false, true);
6099         
6100         if(!row || typeof(row) == 'undefined'){
6101             return;
6102         }
6103         
6104         var cellIndex = cell.dom.cellIndex;
6105         var rowIndex = this.getRowIndex(row);
6106         
6107         // why??? - should these not be based on SelectionModel?
6108         if(this.cellSelection){
6109             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6110         }
6111         
6112         if(this.rowSelection){
6113             this.fireEvent('rowclick', this, row, rowIndex, e);
6114         }
6115         
6116         
6117     },
6118     
6119     onDblClick : function(e,el)
6120     {
6121         var cell = Roo.get(el);
6122         
6123         if(!cell || (!this.CellSelection && !this.RowSelection)){
6124             return;
6125         }
6126         
6127         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6128             cell = cell.findParent('td', false, true);
6129         }
6130         
6131         if(!cell || typeof(cell) == 'undefined'){
6132             return;
6133         }
6134         
6135         var row = cell.findParent('tr', false, true);
6136         
6137         if(!row || typeof(row) == 'undefined'){
6138             return;
6139         }
6140         
6141         var cellIndex = cell.dom.cellIndex;
6142         var rowIndex = this.getRowIndex(row);
6143         
6144         if(this.CellSelection){
6145             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6146         }
6147         
6148         if(this.RowSelection){
6149             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6150         }
6151     },
6152     
6153     sort : function(e,el)
6154     {
6155         var col = Roo.get(el);
6156         
6157         if(!col.hasClass('sortable')){
6158             return;
6159         }
6160         
6161         var sort = col.attr('sort');
6162         var dir = 'ASC';
6163         
6164         if(col.hasClass('glyphicon-arrow-up')){
6165             dir = 'DESC';
6166         }
6167         
6168         this.store.sortInfo = {field : sort, direction : dir};
6169         
6170         if (this.footer) {
6171             Roo.log("calling footer first");
6172             this.footer.onClick('first');
6173         } else {
6174         
6175             this.store.load({ params : { start : 0 } });
6176         }
6177     },
6178     
6179     renderHeader : function()
6180     {
6181         var header = {
6182             tag: 'thead',
6183             cn : []
6184         };
6185         
6186         var cm = this.cm;
6187         
6188         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6189             
6190             var config = cm.config[i];
6191             
6192             var c = {
6193                 tag: 'th',
6194                 style : '',
6195                 html: cm.getColumnHeader(i)
6196             };
6197             
6198             var hh = '';
6199             
6200             if(typeof(config.lgHeader) != 'undefined'){
6201                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6202             }
6203             
6204             if(typeof(config.mdHeader) != 'undefined'){
6205                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6206             }
6207             
6208             if(typeof(config.smHeader) != 'undefined'){
6209                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6210             }
6211             
6212             if(typeof(config.xsHeader) != 'undefined'){
6213                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6214             }
6215             
6216             if(hh.length){
6217                 c.html = hh;
6218             }
6219             
6220             if(typeof(config.tooltip) != 'undefined'){
6221                 c.tooltip = config.tooltip;
6222             }
6223             
6224             if(typeof(config.colspan) != 'undefined'){
6225                 c.colspan = config.colspan;
6226             }
6227             
6228             if(typeof(config.hidden) != 'undefined' && config.hidden){
6229                 c.style += ' display:none;';
6230             }
6231             
6232             if(typeof(config.dataIndex) != 'undefined'){
6233                 c.sort = config.dataIndex;
6234             }
6235             
6236             if(typeof(config.sortable) != 'undefined' && config.sortable){
6237                 c.cls = 'sortable';
6238             }
6239             
6240             if(typeof(config.align) != 'undefined' && config.align.length){
6241                 c.style += ' text-align:' + config.align + ';';
6242             }
6243             
6244             if(typeof(config.width) != 'undefined'){
6245                 c.style += ' width:' + config.width + 'px;';
6246             }
6247             
6248             if(typeof(config.cls) != 'undefined'){
6249                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6250             }
6251             
6252             ['xs','sm','md','lg'].map(function(size){
6253                 
6254                 if(typeof(config[size]) == 'undefined'){
6255                     return;
6256                 }
6257                 
6258                 if (!config[size]) { // 0 = hidden
6259                     c.cls += ' hidden-' + size;
6260                     return;
6261                 }
6262                 
6263                 c.cls += ' col-' + size + '-' + config[size];
6264
6265             });
6266             
6267             header.cn.push(c)
6268         }
6269         
6270         return header;
6271     },
6272     
6273     renderBody : function()
6274     {
6275         var body = {
6276             tag: 'tbody',
6277             cn : [
6278                 {
6279                     tag: 'tr',
6280                     cn : [
6281                         {
6282                             tag : 'td',
6283                             colspan :  this.cm.getColumnCount()
6284                         }
6285                     ]
6286                 }
6287             ]
6288         };
6289         
6290         return body;
6291     },
6292     
6293     renderFooter : function()
6294     {
6295         var footer = {
6296             tag: 'tfoot',
6297             cn : [
6298                 {
6299                     tag: 'tr',
6300                     cn : [
6301                         {
6302                             tag : 'td',
6303                             colspan :  this.cm.getColumnCount()
6304                         }
6305                     ]
6306                 }
6307             ]
6308         };
6309         
6310         return footer;
6311     },
6312     
6313     
6314     
6315     onLoad : function()
6316     {
6317 //        Roo.log('ds onload');
6318         this.clear();
6319         
6320         var _this = this;
6321         var cm = this.cm;
6322         var ds = this.store;
6323         
6324         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6325             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6326             if (_this.store.sortInfo) {
6327                     
6328                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6329                     e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6330                 }
6331                 
6332                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6333                     e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6334                 }
6335             }
6336         });
6337         
6338         var tbody =  this.mainBody;
6339               
6340         if(ds.getCount() > 0){
6341             ds.data.each(function(d,rowIndex){
6342                 var row =  this.renderRow(cm, ds, rowIndex);
6343                 
6344                 tbody.createChild(row);
6345                 
6346                 var _this = this;
6347                 
6348                 if(row.cellObjects.length){
6349                     Roo.each(row.cellObjects, function(r){
6350                         _this.renderCellObject(r);
6351                     })
6352                 }
6353                 
6354             }, this);
6355         }
6356         
6357         Roo.each(this.el.select('tbody td', true).elements, function(e){
6358             e.on('mouseover', _this.onMouseover, _this);
6359         });
6360         
6361         Roo.each(this.el.select('tbody td', true).elements, function(e){
6362             e.on('mouseout', _this.onMouseout, _this);
6363         });
6364         this.fireEvent('rowsrendered', this);
6365         //if(this.loadMask){
6366         //    this.maskEl.hide();
6367         //}
6368         
6369         this.autoSize();
6370     },
6371     
6372     
6373     onUpdate : function(ds,record)
6374     {
6375         this.refreshRow(record);
6376     },
6377     
6378     onRemove : function(ds, record, index, isUpdate){
6379         if(isUpdate !== true){
6380             this.fireEvent("beforerowremoved", this, index, record);
6381         }
6382         var bt = this.mainBody.dom;
6383         
6384         var rows = this.el.select('tbody > tr', true).elements;
6385         
6386         if(typeof(rows[index]) != 'undefined'){
6387             bt.removeChild(rows[index].dom);
6388         }
6389         
6390 //        if(bt.rows[index]){
6391 //            bt.removeChild(bt.rows[index]);
6392 //        }
6393         
6394         if(isUpdate !== true){
6395             //this.stripeRows(index);
6396             //this.syncRowHeights(index, index);
6397             //this.layout();
6398             this.fireEvent("rowremoved", this, index, record);
6399         }
6400     },
6401     
6402     onAdd : function(ds, records, rowIndex)
6403     {
6404         //Roo.log('on Add called');
6405         // - note this does not handle multiple adding very well..
6406         var bt = this.mainBody.dom;
6407         for (var i =0 ; i < records.length;i++) {
6408             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6409             //Roo.log(records[i]);
6410             //Roo.log(this.store.getAt(rowIndex+i));
6411             this.insertRow(this.store, rowIndex + i, false);
6412             return;
6413         }
6414         
6415     },
6416     
6417     
6418     refreshRow : function(record){
6419         var ds = this.store, index;
6420         if(typeof record == 'number'){
6421             index = record;
6422             record = ds.getAt(index);
6423         }else{
6424             index = ds.indexOf(record);
6425         }
6426         this.insertRow(ds, index, true);
6427         this.onRemove(ds, record, index+1, true);
6428         //this.syncRowHeights(index, index);
6429         //this.layout();
6430         this.fireEvent("rowupdated", this, index, record);
6431     },
6432     
6433     insertRow : function(dm, rowIndex, isUpdate){
6434         
6435         if(!isUpdate){
6436             this.fireEvent("beforerowsinserted", this, rowIndex);
6437         }
6438             //var s = this.getScrollState();
6439         var row = this.renderRow(this.cm, this.store, rowIndex);
6440         // insert before rowIndex..
6441         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6442         
6443         var _this = this;
6444                 
6445         if(row.cellObjects.length){
6446             Roo.each(row.cellObjects, function(r){
6447                 _this.renderCellObject(r);
6448             })
6449         }
6450             
6451         if(!isUpdate){
6452             this.fireEvent("rowsinserted", this, rowIndex);
6453             //this.syncRowHeights(firstRow, lastRow);
6454             //this.stripeRows(firstRow);
6455             //this.layout();
6456         }
6457         
6458     },
6459     
6460     
6461     getRowDom : function(rowIndex)
6462     {
6463         var rows = this.el.select('tbody > tr', true).elements;
6464         
6465         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6466         
6467     },
6468     // returns the object tree for a tr..
6469   
6470     
6471     renderRow : function(cm, ds, rowIndex) 
6472     {
6473         
6474         var d = ds.getAt(rowIndex);
6475         
6476         var row = {
6477             tag : 'tr',
6478             cn : []
6479         };
6480             
6481         var cellObjects = [];
6482         
6483         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6484             var config = cm.config[i];
6485             
6486             var renderer = cm.getRenderer(i);
6487             var value = '';
6488             var id = false;
6489             
6490             if(typeof(renderer) !== 'undefined'){
6491                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6492             }
6493             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6494             // and are rendered into the cells after the row is rendered - using the id for the element.
6495             
6496             if(typeof(value) === 'object'){
6497                 id = Roo.id();
6498                 cellObjects.push({
6499                     container : id,
6500                     cfg : value 
6501                 })
6502             }
6503             
6504             var rowcfg = {
6505                 record: d,
6506                 rowIndex : rowIndex,
6507                 colIndex : i,
6508                 rowClass : ''
6509             };
6510
6511             this.fireEvent('rowclass', this, rowcfg);
6512             
6513             var td = {
6514                 tag: 'td',
6515                 cls : rowcfg.rowClass,
6516                 style: '',
6517                 html: (typeof(value) === 'object') ? '' : value
6518             };
6519             
6520             if (id) {
6521                 td.id = id;
6522             }
6523             
6524             if(typeof(config.colspan) != 'undefined'){
6525                 td.colspan = config.colspan;
6526             }
6527             
6528             if(typeof(config.hidden) != 'undefined' && config.hidden){
6529                 td.style += ' display:none;';
6530             }
6531             
6532             if(typeof(config.align) != 'undefined' && config.align.length){
6533                 td.style += ' text-align:' + config.align + ';';
6534             }
6535             
6536             if(typeof(config.width) != 'undefined'){
6537                 td.style += ' width:' +  config.width + 'px;';
6538             }
6539             
6540             if(typeof(config.cursor) != 'undefined'){
6541                 td.style += ' cursor:' +  config.cursor + ';';
6542             }
6543             
6544             if(typeof(config.cls) != 'undefined'){
6545                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6546             }
6547             
6548             ['xs','sm','md','lg'].map(function(size){
6549                 
6550                 if(typeof(config[size]) == 'undefined'){
6551                     return;
6552                 }
6553                 
6554                 if (!config[size]) { // 0 = hidden
6555                     td.cls += ' hidden-' + size;
6556                     return;
6557                 }
6558                 
6559                 td.cls += ' col-' + size + '-' + config[size];
6560
6561             });
6562              
6563             row.cn.push(td);
6564            
6565         }
6566         
6567         row.cellObjects = cellObjects;
6568         
6569         return row;
6570           
6571     },
6572     
6573     
6574     
6575     onBeforeLoad : function()
6576     {
6577         //Roo.log('ds onBeforeLoad');
6578         
6579         //this.clear();
6580         
6581         //if(this.loadMask){
6582         //    this.maskEl.show();
6583         //}
6584     },
6585      /**
6586      * Remove all rows
6587      */
6588     clear : function()
6589     {
6590         this.el.select('tbody', true).first().dom.innerHTML = '';
6591     },
6592     /**
6593      * Show or hide a row.
6594      * @param {Number} rowIndex to show or hide
6595      * @param {Boolean} state hide
6596      */
6597     setRowVisibility : function(rowIndex, state)
6598     {
6599         var bt = this.mainBody.dom;
6600         
6601         var rows = this.el.select('tbody > tr', true).elements;
6602         
6603         if(typeof(rows[rowIndex]) == 'undefined'){
6604             return;
6605         }
6606         rows[rowIndex].dom.style.display = state ? '' : 'none';
6607     },
6608     
6609     
6610     getSelectionModel : function(){
6611         if(!this.selModel){
6612             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6613         }
6614         return this.selModel;
6615     },
6616     /*
6617      * Render the Roo.bootstrap object from renderder
6618      */
6619     renderCellObject : function(r)
6620     {
6621         var _this = this;
6622         
6623         var t = r.cfg.render(r.container);
6624         
6625         if(r.cfg.cn){
6626             Roo.each(r.cfg.cn, function(c){
6627                 var child = {
6628                     container: t.getChildContainer(),
6629                     cfg: c
6630                 };
6631                 _this.renderCellObject(child);
6632             })
6633         }
6634     },
6635     
6636     getRowIndex : function(row)
6637     {
6638         var rowIndex = -1;
6639         
6640         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6641             if(el != row){
6642                 return;
6643             }
6644             
6645             rowIndex = index;
6646         });
6647         
6648         return rowIndex;
6649     },
6650      /**
6651      * Returns the grid's underlying element = used by panel.Grid
6652      * @return {Element} The element
6653      */
6654     getGridEl : function(){
6655         return this.container;
6656     },
6657      /**
6658      * Forces a resize - used by panel.Grid
6659      * @return {Element} The element
6660      */
6661     autoSize : function(){
6662         var ctr = Roo.get(this.container.dom.parentElement);
6663         
6664         var thd = this.getGridEl().select('thead',true).first();
6665         var tbd = this.getGridEl().select('tbody', true).first();
6666         
6667         var cw = Math.max(ctr.getWidth(), this.totalWidth);
6668         if (tbd) {
6669             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6670         }
6671         this.getGridEl().select('tr',true).setWidth(cw);
6672         
6673         return; // we doe not have a view in this design..
6674         if(this.rendered){
6675             this.view.layout();
6676             if(this.view.adjustForScroll){
6677                 this.view.adjustForScroll();
6678             }
6679         }
6680     }
6681 });
6682
6683  
6684
6685  /*
6686  * - LGPL
6687  *
6688  * table cell
6689  * 
6690  */
6691
6692 /**
6693  * @class Roo.bootstrap.TableCell
6694  * @extends Roo.bootstrap.Component
6695  * Bootstrap TableCell class
6696  * @cfg {String} html cell contain text
6697  * @cfg {String} cls cell class
6698  * @cfg {String} tag cell tag (td|th) default td
6699  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6700  * @cfg {String} align Aligns the content in a cell
6701  * @cfg {String} axis Categorizes cells
6702  * @cfg {String} bgcolor Specifies the background color of a cell
6703  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6704  * @cfg {Number} colspan Specifies the number of columns a cell should span
6705  * @cfg {String} headers Specifies one or more header cells a cell is related to
6706  * @cfg {Number} height Sets the height of a cell
6707  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6708  * @cfg {Number} rowspan Sets the number of rows a cell should span
6709  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6710  * @cfg {String} valign Vertical aligns the content in a cell
6711  * @cfg {Number} width Specifies the width of a cell
6712  * 
6713  * @constructor
6714  * Create a new TableCell
6715  * @param {Object} config The config object
6716  */
6717
6718 Roo.bootstrap.TableCell = function(config){
6719     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6720 };
6721
6722 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6723     
6724     html: false,
6725     cls: false,
6726     tag: false,
6727     abbr: false,
6728     align: false,
6729     axis: false,
6730     bgcolor: false,
6731     charoff: false,
6732     colspan: false,
6733     headers: false,
6734     height: false,
6735     nowrap: false,
6736     rowspan: false,
6737     scope: false,
6738     valign: false,
6739     width: false,
6740     
6741     
6742     getAutoCreate : function(){
6743         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6744         
6745         cfg = {
6746             tag: 'td'
6747         };
6748         
6749         if(this.tag){
6750             cfg.tag = this.tag;
6751         }
6752         
6753         if (this.html) {
6754             cfg.html=this.html
6755         }
6756         if (this.cls) {
6757             cfg.cls=this.cls
6758         }
6759         if (this.abbr) {
6760             cfg.abbr=this.abbr
6761         }
6762         if (this.align) {
6763             cfg.align=this.align
6764         }
6765         if (this.axis) {
6766             cfg.axis=this.axis
6767         }
6768         if (this.bgcolor) {
6769             cfg.bgcolor=this.bgcolor
6770         }
6771         if (this.charoff) {
6772             cfg.charoff=this.charoff
6773         }
6774         if (this.colspan) {
6775             cfg.colspan=this.colspan
6776         }
6777         if (this.headers) {
6778             cfg.headers=this.headers
6779         }
6780         if (this.height) {
6781             cfg.height=this.height
6782         }
6783         if (this.nowrap) {
6784             cfg.nowrap=this.nowrap
6785         }
6786         if (this.rowspan) {
6787             cfg.rowspan=this.rowspan
6788         }
6789         if (this.scope) {
6790             cfg.scope=this.scope
6791         }
6792         if (this.valign) {
6793             cfg.valign=this.valign
6794         }
6795         if (this.width) {
6796             cfg.width=this.width
6797         }
6798         
6799         
6800         return cfg;
6801     }
6802    
6803 });
6804
6805  
6806
6807  /*
6808  * - LGPL
6809  *
6810  * table row
6811  * 
6812  */
6813
6814 /**
6815  * @class Roo.bootstrap.TableRow
6816  * @extends Roo.bootstrap.Component
6817  * Bootstrap TableRow class
6818  * @cfg {String} cls row class
6819  * @cfg {String} align Aligns the content in a table row
6820  * @cfg {String} bgcolor Specifies a background color for a table row
6821  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6822  * @cfg {String} valign Vertical aligns the content in a table row
6823  * 
6824  * @constructor
6825  * Create a new TableRow
6826  * @param {Object} config The config object
6827  */
6828
6829 Roo.bootstrap.TableRow = function(config){
6830     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6831 };
6832
6833 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6834     
6835     cls: false,
6836     align: false,
6837     bgcolor: false,
6838     charoff: false,
6839     valign: false,
6840     
6841     getAutoCreate : function(){
6842         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6843         
6844         cfg = {
6845             tag: 'tr'
6846         };
6847             
6848         if(this.cls){
6849             cfg.cls = this.cls;
6850         }
6851         if(this.align){
6852             cfg.align = this.align;
6853         }
6854         if(this.bgcolor){
6855             cfg.bgcolor = this.bgcolor;
6856         }
6857         if(this.charoff){
6858             cfg.charoff = this.charoff;
6859         }
6860         if(this.valign){
6861             cfg.valign = this.valign;
6862         }
6863         
6864         return cfg;
6865     }
6866    
6867 });
6868
6869  
6870
6871  /*
6872  * - LGPL
6873  *
6874  * table body
6875  * 
6876  */
6877
6878 /**
6879  * @class Roo.bootstrap.TableBody
6880  * @extends Roo.bootstrap.Component
6881  * Bootstrap TableBody class
6882  * @cfg {String} cls element class
6883  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6884  * @cfg {String} align Aligns the content inside the element
6885  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6886  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6887  * 
6888  * @constructor
6889  * Create a new TableBody
6890  * @param {Object} config The config object
6891  */
6892
6893 Roo.bootstrap.TableBody = function(config){
6894     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6895 };
6896
6897 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6898     
6899     cls: false,
6900     tag: false,
6901     align: false,
6902     charoff: false,
6903     valign: false,
6904     
6905     getAutoCreate : function(){
6906         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6907         
6908         cfg = {
6909             tag: 'tbody'
6910         };
6911             
6912         if (this.cls) {
6913             cfg.cls=this.cls
6914         }
6915         if(this.tag){
6916             cfg.tag = this.tag;
6917         }
6918         
6919         if(this.align){
6920             cfg.align = this.align;
6921         }
6922         if(this.charoff){
6923             cfg.charoff = this.charoff;
6924         }
6925         if(this.valign){
6926             cfg.valign = this.valign;
6927         }
6928         
6929         return cfg;
6930     }
6931     
6932     
6933 //    initEvents : function()
6934 //    {
6935 //        
6936 //        if(!this.store){
6937 //            return;
6938 //        }
6939 //        
6940 //        this.store = Roo.factory(this.store, Roo.data);
6941 //        this.store.on('load', this.onLoad, this);
6942 //        
6943 //        this.store.load();
6944 //        
6945 //    },
6946 //    
6947 //    onLoad: function () 
6948 //    {   
6949 //        this.fireEvent('load', this);
6950 //    }
6951 //    
6952 //   
6953 });
6954
6955  
6956
6957  /*
6958  * Based on:
6959  * Ext JS Library 1.1.1
6960  * Copyright(c) 2006-2007, Ext JS, LLC.
6961  *
6962  * Originally Released Under LGPL - original licence link has changed is not relivant.
6963  *
6964  * Fork - LGPL
6965  * <script type="text/javascript">
6966  */
6967
6968 // as we use this in bootstrap.
6969 Roo.namespace('Roo.form');
6970  /**
6971  * @class Roo.form.Action
6972  * Internal Class used to handle form actions
6973  * @constructor
6974  * @param {Roo.form.BasicForm} el The form element or its id
6975  * @param {Object} config Configuration options
6976  */
6977
6978  
6979  
6980 // define the action interface
6981 Roo.form.Action = function(form, options){
6982     this.form = form;
6983     this.options = options || {};
6984 };
6985 /**
6986  * Client Validation Failed
6987  * @const 
6988  */
6989 Roo.form.Action.CLIENT_INVALID = 'client';
6990 /**
6991  * Server Validation Failed
6992  * @const 
6993  */
6994 Roo.form.Action.SERVER_INVALID = 'server';
6995  /**
6996  * Connect to Server Failed
6997  * @const 
6998  */
6999 Roo.form.Action.CONNECT_FAILURE = 'connect';
7000 /**
7001  * Reading Data from Server Failed
7002  * @const 
7003  */
7004 Roo.form.Action.LOAD_FAILURE = 'load';
7005
7006 Roo.form.Action.prototype = {
7007     type : 'default',
7008     failureType : undefined,
7009     response : undefined,
7010     result : undefined,
7011
7012     // interface method
7013     run : function(options){
7014
7015     },
7016
7017     // interface method
7018     success : function(response){
7019
7020     },
7021
7022     // interface method
7023     handleResponse : function(response){
7024
7025     },
7026
7027     // default connection failure
7028     failure : function(response){
7029         
7030         this.response = response;
7031         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7032         this.form.afterAction(this, false);
7033     },
7034
7035     processResponse : function(response){
7036         this.response = response;
7037         if(!response.responseText){
7038             return true;
7039         }
7040         this.result = this.handleResponse(response);
7041         return this.result;
7042     },
7043
7044     // utility functions used internally
7045     getUrl : function(appendParams){
7046         var url = this.options.url || this.form.url || this.form.el.dom.action;
7047         if(appendParams){
7048             var p = this.getParams();
7049             if(p){
7050                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7051             }
7052         }
7053         return url;
7054     },
7055
7056     getMethod : function(){
7057         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7058     },
7059
7060     getParams : function(){
7061         var bp = this.form.baseParams;
7062         var p = this.options.params;
7063         if(p){
7064             if(typeof p == "object"){
7065                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7066             }else if(typeof p == 'string' && bp){
7067                 p += '&' + Roo.urlEncode(bp);
7068             }
7069         }else if(bp){
7070             p = Roo.urlEncode(bp);
7071         }
7072         return p;
7073     },
7074
7075     createCallback : function(){
7076         return {
7077             success: this.success,
7078             failure: this.failure,
7079             scope: this,
7080             timeout: (this.form.timeout*1000),
7081             upload: this.form.fileUpload ? this.success : undefined
7082         };
7083     }
7084 };
7085
7086 Roo.form.Action.Submit = function(form, options){
7087     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7088 };
7089
7090 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7091     type : 'submit',
7092
7093     haveProgress : false,
7094     uploadComplete : false,
7095     
7096     // uploadProgress indicator.
7097     uploadProgress : function()
7098     {
7099         if (!this.form.progressUrl) {
7100             return;
7101         }
7102         
7103         if (!this.haveProgress) {
7104             Roo.MessageBox.progress("Uploading", "Uploading");
7105         }
7106         if (this.uploadComplete) {
7107            Roo.MessageBox.hide();
7108            return;
7109         }
7110         
7111         this.haveProgress = true;
7112    
7113         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7114         
7115         var c = new Roo.data.Connection();
7116         c.request({
7117             url : this.form.progressUrl,
7118             params: {
7119                 id : uid
7120             },
7121             method: 'GET',
7122             success : function(req){
7123                //console.log(data);
7124                 var rdata = false;
7125                 var edata;
7126                 try  {
7127                    rdata = Roo.decode(req.responseText)
7128                 } catch (e) {
7129                     Roo.log("Invalid data from server..");
7130                     Roo.log(edata);
7131                     return;
7132                 }
7133                 if (!rdata || !rdata.success) {
7134                     Roo.log(rdata);
7135                     Roo.MessageBox.alert(Roo.encode(rdata));
7136                     return;
7137                 }
7138                 var data = rdata.data;
7139                 
7140                 if (this.uploadComplete) {
7141                    Roo.MessageBox.hide();
7142                    return;
7143                 }
7144                    
7145                 if (data){
7146                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7147                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7148                     );
7149                 }
7150                 this.uploadProgress.defer(2000,this);
7151             },
7152        
7153             failure: function(data) {
7154                 Roo.log('progress url failed ');
7155                 Roo.log(data);
7156             },
7157             scope : this
7158         });
7159            
7160     },
7161     
7162     
7163     run : function()
7164     {
7165         // run get Values on the form, so it syncs any secondary forms.
7166         this.form.getValues();
7167         
7168         var o = this.options;
7169         var method = this.getMethod();
7170         var isPost = method == 'POST';
7171         if(o.clientValidation === false || this.form.isValid()){
7172             
7173             if (this.form.progressUrl) {
7174                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7175                     (new Date() * 1) + '' + Math.random());
7176                     
7177             } 
7178             
7179             
7180             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7181                 form:this.form.el.dom,
7182                 url:this.getUrl(!isPost),
7183                 method: method,
7184                 params:isPost ? this.getParams() : null,
7185                 isUpload: this.form.fileUpload
7186             }));
7187             
7188             this.uploadProgress();
7189
7190         }else if (o.clientValidation !== false){ // client validation failed
7191             this.failureType = Roo.form.Action.CLIENT_INVALID;
7192             this.form.afterAction(this, false);
7193         }
7194     },
7195
7196     success : function(response)
7197     {
7198         this.uploadComplete= true;
7199         if (this.haveProgress) {
7200             Roo.MessageBox.hide();
7201         }
7202         
7203         
7204         var result = this.processResponse(response);
7205         if(result === true || result.success){
7206             this.form.afterAction(this, true);
7207             return;
7208         }
7209         if(result.errors){
7210             this.form.markInvalid(result.errors);
7211             this.failureType = Roo.form.Action.SERVER_INVALID;
7212         }
7213         this.form.afterAction(this, false);
7214     },
7215     failure : function(response)
7216     {
7217         this.uploadComplete= true;
7218         if (this.haveProgress) {
7219             Roo.MessageBox.hide();
7220         }
7221         
7222         this.response = response;
7223         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7224         this.form.afterAction(this, false);
7225     },
7226     
7227     handleResponse : function(response){
7228         if(this.form.errorReader){
7229             var rs = this.form.errorReader.read(response);
7230             var errors = [];
7231             if(rs.records){
7232                 for(var i = 0, len = rs.records.length; i < len; i++) {
7233                     var r = rs.records[i];
7234                     errors[i] = r.data;
7235                 }
7236             }
7237             if(errors.length < 1){
7238                 errors = null;
7239             }
7240             return {
7241                 success : rs.success,
7242                 errors : errors
7243             };
7244         }
7245         var ret = false;
7246         try {
7247             ret = Roo.decode(response.responseText);
7248         } catch (e) {
7249             ret = {
7250                 success: false,
7251                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7252                 errors : []
7253             };
7254         }
7255         return ret;
7256         
7257     }
7258 });
7259
7260
7261 Roo.form.Action.Load = function(form, options){
7262     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7263     this.reader = this.form.reader;
7264 };
7265
7266 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7267     type : 'load',
7268
7269     run : function(){
7270         
7271         Roo.Ajax.request(Roo.apply(
7272                 this.createCallback(), {
7273                     method:this.getMethod(),
7274                     url:this.getUrl(false),
7275                     params:this.getParams()
7276         }));
7277     },
7278
7279     success : function(response){
7280         
7281         var result = this.processResponse(response);
7282         if(result === true || !result.success || !result.data){
7283             this.failureType = Roo.form.Action.LOAD_FAILURE;
7284             this.form.afterAction(this, false);
7285             return;
7286         }
7287         this.form.clearInvalid();
7288         this.form.setValues(result.data);
7289         this.form.afterAction(this, true);
7290     },
7291
7292     handleResponse : function(response){
7293         if(this.form.reader){
7294             var rs = this.form.reader.read(response);
7295             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7296             return {
7297                 success : rs.success,
7298                 data : data
7299             };
7300         }
7301         return Roo.decode(response.responseText);
7302     }
7303 });
7304
7305 Roo.form.Action.ACTION_TYPES = {
7306     'load' : Roo.form.Action.Load,
7307     'submit' : Roo.form.Action.Submit
7308 };/*
7309  * - LGPL
7310  *
7311  * form
7312  * 
7313  */
7314
7315 /**
7316  * @class Roo.bootstrap.Form
7317  * @extends Roo.bootstrap.Component
7318  * Bootstrap Form class
7319  * @cfg {String} method  GET | POST (default POST)
7320  * @cfg {String} labelAlign top | left (default top)
7321  * @cfg {String} align left  | right - for navbars
7322  * @cfg {Boolean} loadMask load mask when submit (default true)
7323
7324  * 
7325  * @constructor
7326  * Create a new Form
7327  * @param {Object} config The config object
7328  */
7329
7330
7331 Roo.bootstrap.Form = function(config){
7332     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7333     this.addEvents({
7334         /**
7335          * @event clientvalidation
7336          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7337          * @param {Form} this
7338          * @param {Boolean} valid true if the form has passed client-side validation
7339          */
7340         clientvalidation: true,
7341         /**
7342          * @event beforeaction
7343          * Fires before any action is performed. Return false to cancel the action.
7344          * @param {Form} this
7345          * @param {Action} action The action to be performed
7346          */
7347         beforeaction: true,
7348         /**
7349          * @event actionfailed
7350          * Fires when an action fails.
7351          * @param {Form} this
7352          * @param {Action} action The action that failed
7353          */
7354         actionfailed : true,
7355         /**
7356          * @event actioncomplete
7357          * Fires when an action is completed.
7358          * @param {Form} this
7359          * @param {Action} action The action that completed
7360          */
7361         actioncomplete : true
7362     });
7363     
7364 };
7365
7366 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7367       
7368      /**
7369      * @cfg {String} method
7370      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7371      */
7372     method : 'POST',
7373     /**
7374      * @cfg {String} url
7375      * The URL to use for form actions if one isn't supplied in the action options.
7376      */
7377     /**
7378      * @cfg {Boolean} fileUpload
7379      * Set to true if this form is a file upload.
7380      */
7381      
7382     /**
7383      * @cfg {Object} baseParams
7384      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7385      */
7386       
7387     /**
7388      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7389      */
7390     timeout: 30,
7391     /**
7392      * @cfg {Sting} align (left|right) for navbar forms
7393      */
7394     align : 'left',
7395
7396     // private
7397     activeAction : null,
7398  
7399     /**
7400      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7401      * element by passing it or its id or mask the form itself by passing in true.
7402      * @type Mixed
7403      */
7404     waitMsgTarget : false,
7405     
7406     loadMask : true,
7407     
7408     getAutoCreate : function(){
7409         
7410         var cfg = {
7411             tag: 'form',
7412             method : this.method || 'POST',
7413             id : this.id || Roo.id(),
7414             cls : ''
7415         };
7416         if (this.parent().xtype.match(/^Nav/)) {
7417             cfg.cls = 'navbar-form navbar-' + this.align;
7418             
7419         }
7420         
7421         if (this.labelAlign == 'left' ) {
7422             cfg.cls += ' form-horizontal';
7423         }
7424         
7425         
7426         return cfg;
7427     },
7428     initEvents : function()
7429     {
7430         this.el.on('submit', this.onSubmit, this);
7431         // this was added as random key presses on the form where triggering form submit.
7432         this.el.on('keypress', function(e) {
7433             if (e.getCharCode() != 13) {
7434                 return true;
7435             }
7436             // we might need to allow it for textareas.. and some other items.
7437             // check e.getTarget().
7438             
7439             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7440                 return true;
7441             }
7442         
7443             Roo.log("keypress blocked");
7444             
7445             e.preventDefault();
7446             return false;
7447         });
7448         
7449     },
7450     // private
7451     onSubmit : function(e){
7452         e.stopEvent();
7453     },
7454     
7455      /**
7456      * Returns true if client-side validation on the form is successful.
7457      * @return Boolean
7458      */
7459     isValid : function(){
7460         var items = this.getItems();
7461         var valid = true;
7462         items.each(function(f){
7463            if(!f.validate()){
7464                valid = false;
7465                
7466            }
7467         });
7468         return valid;
7469     },
7470     /**
7471      * Returns true if any fields in this form have changed since their original load.
7472      * @return Boolean
7473      */
7474     isDirty : function(){
7475         var dirty = false;
7476         var items = this.getItems();
7477         items.each(function(f){
7478            if(f.isDirty()){
7479                dirty = true;
7480                return false;
7481            }
7482            return true;
7483         });
7484         return dirty;
7485     },
7486      /**
7487      * Performs a predefined action (submit or load) or custom actions you define on this form.
7488      * @param {String} actionName The name of the action type
7489      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7490      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7491      * accept other config options):
7492      * <pre>
7493 Property          Type             Description
7494 ----------------  ---------------  ----------------------------------------------------------------------------------
7495 url               String           The url for the action (defaults to the form's url)
7496 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7497 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7498 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7499                                    validate the form on the client (defaults to false)
7500      * </pre>
7501      * @return {BasicForm} this
7502      */
7503     doAction : function(action, options){
7504         if(typeof action == 'string'){
7505             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7506         }
7507         if(this.fireEvent('beforeaction', this, action) !== false){
7508             this.beforeAction(action);
7509             action.run.defer(100, action);
7510         }
7511         return this;
7512     },
7513     
7514     // private
7515     beforeAction : function(action){
7516         var o = action.options;
7517         
7518         if(this.loadMask){
7519             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7520         }
7521         // not really supported yet.. ??
7522         
7523         //if(this.waitMsgTarget === true){
7524         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7525         //}else if(this.waitMsgTarget){
7526         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7527         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7528         //}else {
7529         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7530        // }
7531          
7532     },
7533
7534     // private
7535     afterAction : function(action, success){
7536         this.activeAction = null;
7537         var o = action.options;
7538         
7539         //if(this.waitMsgTarget === true){
7540             this.el.unmask();
7541         //}else if(this.waitMsgTarget){
7542         //    this.waitMsgTarget.unmask();
7543         //}else{
7544         //    Roo.MessageBox.updateProgress(1);
7545         //    Roo.MessageBox.hide();
7546        // }
7547         // 
7548         if(success){
7549             if(o.reset){
7550                 this.reset();
7551             }
7552             Roo.callback(o.success, o.scope, [this, action]);
7553             this.fireEvent('actioncomplete', this, action);
7554             
7555         }else{
7556             
7557             // failure condition..
7558             // we have a scenario where updates need confirming.
7559             // eg. if a locking scenario exists..
7560             // we look for { errors : { needs_confirm : true }} in the response.
7561             if (
7562                 (typeof(action.result) != 'undefined')  &&
7563                 (typeof(action.result.errors) != 'undefined')  &&
7564                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7565            ){
7566                 var _t = this;
7567                 Roo.log("not supported yet");
7568                  /*
7569                 
7570                 Roo.MessageBox.confirm(
7571                     "Change requires confirmation",
7572                     action.result.errorMsg,
7573                     function(r) {
7574                         if (r != 'yes') {
7575                             return;
7576                         }
7577                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7578                     }
7579                     
7580                 );
7581                 */
7582                 
7583                 
7584                 return;
7585             }
7586             
7587             Roo.callback(o.failure, o.scope, [this, action]);
7588             // show an error message if no failed handler is set..
7589             if (!this.hasListener('actionfailed')) {
7590                 Roo.log("need to add dialog support");
7591                 /*
7592                 Roo.MessageBox.alert("Error",
7593                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7594                         action.result.errorMsg :
7595                         "Saving Failed, please check your entries or try again"
7596                 );
7597                 */
7598             }
7599             
7600             this.fireEvent('actionfailed', this, action);
7601         }
7602         
7603     },
7604     /**
7605      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7606      * @param {String} id The value to search for
7607      * @return Field
7608      */
7609     findField : function(id){
7610         var items = this.getItems();
7611         var field = items.get(id);
7612         if(!field){
7613              items.each(function(f){
7614                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7615                     field = f;
7616                     return false;
7617                 }
7618                 return true;
7619             });
7620         }
7621         return field || null;
7622     },
7623      /**
7624      * Mark fields in this form invalid in bulk.
7625      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7626      * @return {BasicForm} this
7627      */
7628     markInvalid : function(errors){
7629         if(errors instanceof Array){
7630             for(var i = 0, len = errors.length; i < len; i++){
7631                 var fieldError = errors[i];
7632                 var f = this.findField(fieldError.id);
7633                 if(f){
7634                     f.markInvalid(fieldError.msg);
7635                 }
7636             }
7637         }else{
7638             var field, id;
7639             for(id in errors){
7640                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7641                     field.markInvalid(errors[id]);
7642                 }
7643             }
7644         }
7645         //Roo.each(this.childForms || [], function (f) {
7646         //    f.markInvalid(errors);
7647         //});
7648         
7649         return this;
7650     },
7651
7652     /**
7653      * Set values for fields in this form in bulk.
7654      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7655      * @return {BasicForm} this
7656      */
7657     setValues : function(values){
7658         if(values instanceof Array){ // array of objects
7659             for(var i = 0, len = values.length; i < len; i++){
7660                 var v = values[i];
7661                 var f = this.findField(v.id);
7662                 if(f){
7663                     f.setValue(v.value);
7664                     if(this.trackResetOnLoad){
7665                         f.originalValue = f.getValue();
7666                     }
7667                 }
7668             }
7669         }else{ // object hash
7670             var field, id;
7671             for(id in values){
7672                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7673                     
7674                     if (field.setFromData && 
7675                         field.valueField && 
7676                         field.displayField &&
7677                         // combos' with local stores can 
7678                         // be queried via setValue()
7679                         // to set their value..
7680                         (field.store && !field.store.isLocal)
7681                         ) {
7682                         // it's a combo
7683                         var sd = { };
7684                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7685                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7686                         field.setFromData(sd);
7687                         
7688                     } else {
7689                         field.setValue(values[id]);
7690                     }
7691                     
7692                     
7693                     if(this.trackResetOnLoad){
7694                         field.originalValue = field.getValue();
7695                     }
7696                 }
7697             }
7698         }
7699          
7700         //Roo.each(this.childForms || [], function (f) {
7701         //    f.setValues(values);
7702         //});
7703                 
7704         return this;
7705     },
7706
7707     /**
7708      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7709      * they are returned as an array.
7710      * @param {Boolean} asString
7711      * @return {Object}
7712      */
7713     getValues : function(asString){
7714         //if (this.childForms) {
7715             // copy values from the child forms
7716         //    Roo.each(this.childForms, function (f) {
7717         //        this.setValues(f.getValues());
7718         //    }, this);
7719         //}
7720         
7721         
7722         
7723         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7724         if(asString === true){
7725             return fs;
7726         }
7727         return Roo.urlDecode(fs);
7728     },
7729     
7730     /**
7731      * Returns the fields in this form as an object with key/value pairs. 
7732      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7733      * @return {Object}
7734      */
7735     getFieldValues : function(with_hidden)
7736     {
7737         var items = this.getItems();
7738         var ret = {};
7739         items.each(function(f){
7740             if (!f.getName()) {
7741                 return;
7742             }
7743             var v = f.getValue();
7744             if (f.inputType =='radio') {
7745                 if (typeof(ret[f.getName()]) == 'undefined') {
7746                     ret[f.getName()] = ''; // empty..
7747                 }
7748                 
7749                 if (!f.el.dom.checked) {
7750                     return;
7751                     
7752                 }
7753                 v = f.el.dom.value;
7754                 
7755             }
7756             
7757             // not sure if this supported any more..
7758             if ((typeof(v) == 'object') && f.getRawValue) {
7759                 v = f.getRawValue() ; // dates..
7760             }
7761             // combo boxes where name != hiddenName...
7762             if (f.name != f.getName()) {
7763                 ret[f.name] = f.getRawValue();
7764             }
7765             ret[f.getName()] = v;
7766         });
7767         
7768         return ret;
7769     },
7770
7771     /**
7772      * Clears all invalid messages in this form.
7773      * @return {BasicForm} this
7774      */
7775     clearInvalid : function(){
7776         var items = this.getItems();
7777         
7778         items.each(function(f){
7779            f.clearInvalid();
7780         });
7781         
7782         
7783         
7784         return this;
7785     },
7786
7787     /**
7788      * Resets this form.
7789      * @return {BasicForm} this
7790      */
7791     reset : function(){
7792         var items = this.getItems();
7793         items.each(function(f){
7794             f.reset();
7795         });
7796         
7797         Roo.each(this.childForms || [], function (f) {
7798             f.reset();
7799         });
7800        
7801         
7802         return this;
7803     },
7804     getItems : function()
7805     {
7806         var r=new Roo.util.MixedCollection(false, function(o){
7807             return o.id || (o.id = Roo.id());
7808         });
7809         var iter = function(el) {
7810             if (el.inputEl) {
7811                 r.add(el);
7812             }
7813             if (!el.items) {
7814                 return;
7815             }
7816             Roo.each(el.items,function(e) {
7817                 iter(e);
7818             });
7819             
7820             
7821         };
7822         
7823         iter(this);
7824         return r;
7825         
7826         
7827         
7828         
7829     }
7830     
7831 });
7832
7833  
7834 /*
7835  * Based on:
7836  * Ext JS Library 1.1.1
7837  * Copyright(c) 2006-2007, Ext JS, LLC.
7838  *
7839  * Originally Released Under LGPL - original licence link has changed is not relivant.
7840  *
7841  * Fork - LGPL
7842  * <script type="text/javascript">
7843  */
7844 /**
7845  * @class Roo.form.VTypes
7846  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7847  * @singleton
7848  */
7849 Roo.form.VTypes = function(){
7850     // closure these in so they are only created once.
7851     var alpha = /^[a-zA-Z_]+$/;
7852     var alphanum = /^[a-zA-Z0-9_]+$/;
7853     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7854     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7855
7856     // All these messages and functions are configurable
7857     return {
7858         /**
7859          * The function used to validate email addresses
7860          * @param {String} value The email address
7861          */
7862         'email' : function(v){
7863             return email.test(v);
7864         },
7865         /**
7866          * The error text to display when the email validation function returns false
7867          * @type String
7868          */
7869         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7870         /**
7871          * The keystroke filter mask to be applied on email input
7872          * @type RegExp
7873          */
7874         'emailMask' : /[a-z0-9_\.\-@]/i,
7875
7876         /**
7877          * The function used to validate URLs
7878          * @param {String} value The URL
7879          */
7880         'url' : function(v){
7881             return url.test(v);
7882         },
7883         /**
7884          * The error text to display when the url validation function returns false
7885          * @type String
7886          */
7887         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7888         
7889         /**
7890          * The function used to validate alpha values
7891          * @param {String} value The value
7892          */
7893         'alpha' : function(v){
7894             return alpha.test(v);
7895         },
7896         /**
7897          * The error text to display when the alpha validation function returns false
7898          * @type String
7899          */
7900         'alphaText' : 'This field should only contain letters and _',
7901         /**
7902          * The keystroke filter mask to be applied on alpha input
7903          * @type RegExp
7904          */
7905         'alphaMask' : /[a-z_]/i,
7906
7907         /**
7908          * The function used to validate alphanumeric values
7909          * @param {String} value The value
7910          */
7911         'alphanum' : function(v){
7912             return alphanum.test(v);
7913         },
7914         /**
7915          * The error text to display when the alphanumeric validation function returns false
7916          * @type String
7917          */
7918         'alphanumText' : 'This field should only contain letters, numbers and _',
7919         /**
7920          * The keystroke filter mask to be applied on alphanumeric input
7921          * @type RegExp
7922          */
7923         'alphanumMask' : /[a-z0-9_]/i
7924     };
7925 }();/*
7926  * - LGPL
7927  *
7928  * Input
7929  * 
7930  */
7931
7932 /**
7933  * @class Roo.bootstrap.Input
7934  * @extends Roo.bootstrap.Component
7935  * Bootstrap Input class
7936  * @cfg {Boolean} disabled is it disabled
7937  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7938  * @cfg {String} name name of the input
7939  * @cfg {string} fieldLabel - the label associated
7940  * @cfg {string} placeholder - placeholder to put in text.
7941  * @cfg {string}  before - input group add on before
7942  * @cfg {string} after - input group add on after
7943  * @cfg {string} size - (lg|sm) or leave empty..
7944  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7945  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7946  * @cfg {Number} md colspan out of 12 for computer-sized screens
7947  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7948  * @cfg {string} value default value of the input
7949  * @cfg {Number} labelWidth set the width of label (0-12)
7950  * @cfg {String} labelAlign (top|left)
7951  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7952  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7953
7954  * @cfg {String} align (left|center|right) Default left
7955  * @cfg {Boolean} forceFeedback (true|false) Default false
7956  * 
7957  * 
7958  * 
7959  * 
7960  * @constructor
7961  * Create a new Input
7962  * @param {Object} config The config object
7963  */
7964
7965 Roo.bootstrap.Input = function(config){
7966     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7967    
7968         this.addEvents({
7969             /**
7970              * @event focus
7971              * Fires when this field receives input focus.
7972              * @param {Roo.form.Field} this
7973              */
7974             focus : true,
7975             /**
7976              * @event blur
7977              * Fires when this field loses input focus.
7978              * @param {Roo.form.Field} this
7979              */
7980             blur : true,
7981             /**
7982              * @event specialkey
7983              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7984              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7985              * @param {Roo.form.Field} this
7986              * @param {Roo.EventObject} e The event object
7987              */
7988             specialkey : true,
7989             /**
7990              * @event change
7991              * Fires just before the field blurs if the field value has changed.
7992              * @param {Roo.form.Field} this
7993              * @param {Mixed} newValue The new value
7994              * @param {Mixed} oldValue The original value
7995              */
7996             change : true,
7997             /**
7998              * @event invalid
7999              * Fires after the field has been marked as invalid.
8000              * @param {Roo.form.Field} this
8001              * @param {String} msg The validation message
8002              */
8003             invalid : true,
8004             /**
8005              * @event valid
8006              * Fires after the field has been validated with no errors.
8007              * @param {Roo.form.Field} this
8008              */
8009             valid : true,
8010              /**
8011              * @event keyup
8012              * Fires after the key up
8013              * @param {Roo.form.Field} this
8014              * @param {Roo.EventObject}  e The event Object
8015              */
8016             keyup : true
8017         });
8018 };
8019
8020 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8021      /**
8022      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8023       automatic validation (defaults to "keyup").
8024      */
8025     validationEvent : "keyup",
8026      /**
8027      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8028      */
8029     validateOnBlur : true,
8030     /**
8031      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8032      */
8033     validationDelay : 250,
8034      /**
8035      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8036      */
8037     focusClass : "x-form-focus",  // not needed???
8038     
8039        
8040     /**
8041      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8042      */
8043     invalidClass : "has-warning",
8044     
8045     /**
8046      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8047      */
8048     validClass : "has-success",
8049     
8050     /**
8051      * @cfg {Boolean} hasFeedback (true|false) default true
8052      */
8053     hasFeedback : true,
8054     
8055     /**
8056      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8057      */
8058     invalidFeedbackClass : "glyphicon-warning-sign",
8059     
8060     /**
8061      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8062      */
8063     validFeedbackClass : "glyphicon-ok",
8064     
8065     /**
8066      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8067      */
8068     selectOnFocus : false,
8069     
8070      /**
8071      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8072      */
8073     maskRe : null,
8074        /**
8075      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8076      */
8077     vtype : null,
8078     
8079       /**
8080      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8081      */
8082     disableKeyFilter : false,
8083     
8084        /**
8085      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8086      */
8087     disabled : false,
8088      /**
8089      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8090      */
8091     allowBlank : true,
8092     /**
8093      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8094      */
8095     blankText : "This field is required",
8096     
8097      /**
8098      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8099      */
8100     minLength : 0,
8101     /**
8102      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8103      */
8104     maxLength : Number.MAX_VALUE,
8105     /**
8106      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8107      */
8108     minLengthText : "The minimum length for this field is {0}",
8109     /**
8110      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8111      */
8112     maxLengthText : "The maximum length for this field is {0}",
8113   
8114     
8115     /**
8116      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8117      * If available, this function will be called only after the basic validators all return true, and will be passed the
8118      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8119      */
8120     validator : null,
8121     /**
8122      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8123      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8124      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8125      */
8126     regex : null,
8127     /**
8128      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8129      */
8130     regexText : "",
8131     
8132     autocomplete: false,
8133     
8134     
8135     fieldLabel : '',
8136     inputType : 'text',
8137     
8138     name : false,
8139     placeholder: false,
8140     before : false,
8141     after : false,
8142     size : false,
8143     hasFocus : false,
8144     preventMark: false,
8145     isFormField : true,
8146     value : '',
8147     labelWidth : 2,
8148     labelAlign : false,
8149     readOnly : false,
8150     align : false,
8151     formatedValue : false,
8152     forceFeedback : false,
8153     
8154     parentLabelAlign : function()
8155     {
8156         var parent = this;
8157         while (parent.parent()) {
8158             parent = parent.parent();
8159             if (typeof(parent.labelAlign) !='undefined') {
8160                 return parent.labelAlign;
8161             }
8162         }
8163         return 'left';
8164         
8165     },
8166     
8167     getAutoCreate : function(){
8168         
8169         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8170         
8171         var id = Roo.id();
8172         
8173         var cfg = {};
8174         
8175         if(this.inputType != 'hidden'){
8176             cfg.cls = 'form-group' //input-group
8177         }
8178         
8179         var input =  {
8180             tag: 'input',
8181             id : id,
8182             type : this.inputType,
8183             value : this.value,
8184             cls : 'form-control',
8185             placeholder : this.placeholder || '',
8186             autocomplete : this.autocomplete || 'new-password'
8187         };
8188         
8189         
8190         if(this.align){
8191             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8192         }
8193         
8194         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8195             input.maxLength = this.maxLength;
8196         }
8197         
8198         if (this.disabled) {
8199             input.disabled=true;
8200         }
8201         
8202         if (this.readOnly) {
8203             input.readonly=true;
8204         }
8205         
8206         if (this.name) {
8207             input.name = this.name;
8208         }
8209         if (this.size) {
8210             input.cls += ' input-' + this.size;
8211         }
8212         var settings=this;
8213         ['xs','sm','md','lg'].map(function(size){
8214             if (settings[size]) {
8215                 cfg.cls += ' col-' + size + '-' + settings[size];
8216             }
8217         });
8218         
8219         var inputblock = input;
8220         
8221         var feedback = {
8222             tag: 'span',
8223             cls: 'glyphicon form-control-feedback'
8224         };
8225             
8226         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8227             
8228             inputblock = {
8229                 cls : 'has-feedback',
8230                 cn :  [
8231                     input,
8232                     feedback
8233                 ] 
8234             };  
8235         }
8236         
8237         if (this.before || this.after) {
8238             
8239             inputblock = {
8240                 cls : 'input-group',
8241                 cn :  [] 
8242             };
8243             
8244             if (this.before && typeof(this.before) == 'string') {
8245                 
8246                 inputblock.cn.push({
8247                     tag :'span',
8248                     cls : 'roo-input-before input-group-addon',
8249                     html : this.before
8250                 });
8251             }
8252             if (this.before && typeof(this.before) == 'object') {
8253                 this.before = Roo.factory(this.before);
8254                 
8255                 inputblock.cn.push({
8256                     tag :'span',
8257                     cls : 'roo-input-before input-group-' +
8258                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8259                 });
8260             }
8261             
8262             inputblock.cn.push(input);
8263             
8264             if (this.after && typeof(this.after) == 'string') {
8265                 inputblock.cn.push({
8266                     tag :'span',
8267                     cls : 'roo-input-after input-group-addon',
8268                     html : this.after
8269                 });
8270             }
8271             if (this.after && typeof(this.after) == 'object') {
8272                 this.after = Roo.factory(this.after);
8273                 
8274                 inputblock.cn.push({
8275                     tag :'span',
8276                     cls : 'roo-input-after input-group-' +
8277                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8278                 });
8279             }
8280             
8281             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8282                 inputblock.cls += ' has-feedback';
8283                 inputblock.cn.push(feedback);
8284             }
8285         };
8286         
8287         if (align ==='left' && this.fieldLabel.length) {
8288                 
8289                 cfg.cn = [
8290                     
8291                     {
8292                         tag: 'label',
8293                         'for' :  id,
8294                         cls : 'control-label col-sm-' + this.labelWidth,
8295                         html : this.fieldLabel
8296                         
8297                     },
8298                     {
8299                         cls : "col-sm-" + (12 - this.labelWidth), 
8300                         cn: [
8301                             inputblock
8302                         ]
8303                     }
8304                     
8305                 ];
8306         } else if ( this.fieldLabel.length) {
8307                 
8308                  cfg.cn = [
8309                    
8310                     {
8311                         tag: 'label',
8312                         //cls : 'input-group-addon',
8313                         html : this.fieldLabel
8314                         
8315                     },
8316                     
8317                     inputblock
8318                     
8319                 ];
8320
8321         } else {
8322             
8323                 cfg.cn = [
8324                     
8325                         inputblock
8326                     
8327                 ];
8328                 
8329                 
8330         };
8331         
8332         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8333            cfg.cls += ' navbar-form';
8334         }
8335         
8336         return cfg;
8337         
8338     },
8339     /**
8340      * return the real input element.
8341      */
8342     inputEl: function ()
8343     {
8344         return this.el.select('input.form-control',true).first();
8345     },
8346     
8347     tooltipEl : function()
8348     {
8349         return this.inputEl();
8350     },
8351     
8352     setDisabled : function(v)
8353     {
8354         var i  = this.inputEl().dom;
8355         if (!v) {
8356             i.removeAttribute('disabled');
8357             return;
8358             
8359         }
8360         i.setAttribute('disabled','true');
8361     },
8362     initEvents : function()
8363     {
8364           
8365         this.inputEl().on("keydown" , this.fireKey,  this);
8366         this.inputEl().on("focus", this.onFocus,  this);
8367         this.inputEl().on("blur", this.onBlur,  this);
8368         
8369         this.inputEl().relayEvent('keyup', this);
8370  
8371         // reference to original value for reset
8372         this.originalValue = this.getValue();
8373         //Roo.form.TextField.superclass.initEvents.call(this);
8374         if(this.validationEvent == 'keyup'){
8375             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8376             this.inputEl().on('keyup', this.filterValidation, this);
8377         }
8378         else if(this.validationEvent !== false){
8379             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8380         }
8381         
8382         if(this.selectOnFocus){
8383             this.on("focus", this.preFocus, this);
8384             
8385         }
8386         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8387             this.inputEl().on("keypress", this.filterKeys, this);
8388         }
8389        /* if(this.grow){
8390             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8391             this.el.on("click", this.autoSize,  this);
8392         }
8393         */
8394         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8395             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8396         }
8397         
8398         if (typeof(this.before) == 'object') {
8399             this.before.render(this.el.select('.roo-input-before',true).first());
8400         }
8401         if (typeof(this.after) == 'object') {
8402             this.after.render(this.el.select('.roo-input-after',true).first());
8403         }
8404         
8405         
8406     },
8407     filterValidation : function(e){
8408         if(!e.isNavKeyPress()){
8409             this.validationTask.delay(this.validationDelay);
8410         }
8411     },
8412      /**
8413      * Validates the field value
8414      * @return {Boolean} True if the value is valid, else false
8415      */
8416     validate : function(){
8417         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8418         if(this.disabled || this.validateValue(this.getRawValue())){
8419             this.markValid();
8420             return true;
8421         }
8422         
8423         this.markInvalid();
8424         return false;
8425     },
8426     
8427     
8428     /**
8429      * Validates a value according to the field's validation rules and marks the field as invalid
8430      * if the validation fails
8431      * @param {Mixed} value The value to validate
8432      * @return {Boolean} True if the value is valid, else false
8433      */
8434     validateValue : function(value){
8435         if(value.length < 1)  { // if it's blank
8436             if(this.allowBlank){
8437                 return true;
8438             }
8439             return false;
8440         }
8441         
8442         if(value.length < this.minLength){
8443             return false;
8444         }
8445         if(value.length > this.maxLength){
8446             return false;
8447         }
8448         if(this.vtype){
8449             var vt = Roo.form.VTypes;
8450             if(!vt[this.vtype](value, this)){
8451                 return false;
8452             }
8453         }
8454         if(typeof this.validator == "function"){
8455             var msg = this.validator(value);
8456             if(msg !== true){
8457                 return false;
8458             }
8459         }
8460         
8461         if(this.regex && !this.regex.test(value)){
8462             return false;
8463         }
8464         
8465         return true;
8466     },
8467
8468     
8469     
8470      // private
8471     fireKey : function(e){
8472         //Roo.log('field ' + e.getKey());
8473         if(e.isNavKeyPress()){
8474             this.fireEvent("specialkey", this, e);
8475         }
8476     },
8477     focus : function (selectText){
8478         if(this.rendered){
8479             this.inputEl().focus();
8480             if(selectText === true){
8481                 this.inputEl().dom.select();
8482             }
8483         }
8484         return this;
8485     } ,
8486     
8487     onFocus : function(){
8488         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8489            // this.el.addClass(this.focusClass);
8490         }
8491         if(!this.hasFocus){
8492             this.hasFocus = true;
8493             this.startValue = this.getValue();
8494             this.fireEvent("focus", this);
8495         }
8496     },
8497     
8498     beforeBlur : Roo.emptyFn,
8499
8500     
8501     // private
8502     onBlur : function(){
8503         this.beforeBlur();
8504         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8505             //this.el.removeClass(this.focusClass);
8506         }
8507         this.hasFocus = false;
8508         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8509             this.validate();
8510         }
8511         var v = this.getValue();
8512         if(String(v) !== String(this.startValue)){
8513             this.fireEvent('change', this, v, this.startValue);
8514         }
8515         this.fireEvent("blur", this);
8516     },
8517     
8518     /**
8519      * Resets the current field value to the originally loaded value and clears any validation messages
8520      */
8521     reset : function(){
8522         this.setValue(this.originalValue);
8523         this.validate();
8524     },
8525      /**
8526      * Returns the name of the field
8527      * @return {Mixed} name The name field
8528      */
8529     getName: function(){
8530         return this.name;
8531     },
8532      /**
8533      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8534      * @return {Mixed} value The field value
8535      */
8536     getValue : function(){
8537         
8538         var v = this.inputEl().getValue();
8539         
8540         return v;
8541     },
8542     /**
8543      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8544      * @return {Mixed} value The field value
8545      */
8546     getRawValue : function(){
8547         var v = this.inputEl().getValue();
8548         
8549         return v;
8550     },
8551     
8552     /**
8553      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8554      * @param {Mixed} value The value to set
8555      */
8556     setRawValue : function(v){
8557         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8558     },
8559     
8560     selectText : function(start, end){
8561         var v = this.getRawValue();
8562         if(v.length > 0){
8563             start = start === undefined ? 0 : start;
8564             end = end === undefined ? v.length : end;
8565             var d = this.inputEl().dom;
8566             if(d.setSelectionRange){
8567                 d.setSelectionRange(start, end);
8568             }else if(d.createTextRange){
8569                 var range = d.createTextRange();
8570                 range.moveStart("character", start);
8571                 range.moveEnd("character", v.length-end);
8572                 range.select();
8573             }
8574         }
8575     },
8576     
8577     /**
8578      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8579      * @param {Mixed} value The value to set
8580      */
8581     setValue : function(v){
8582         this.value = v;
8583         if(this.rendered){
8584             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8585             this.validate();
8586         }
8587     },
8588     
8589     /*
8590     processValue : function(value){
8591         if(this.stripCharsRe){
8592             var newValue = value.replace(this.stripCharsRe, '');
8593             if(newValue !== value){
8594                 this.setRawValue(newValue);
8595                 return newValue;
8596             }
8597         }
8598         return value;
8599     },
8600   */
8601     preFocus : function(){
8602         
8603         if(this.selectOnFocus){
8604             this.inputEl().dom.select();
8605         }
8606     },
8607     filterKeys : function(e){
8608         var k = e.getKey();
8609         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8610             return;
8611         }
8612         var c = e.getCharCode(), cc = String.fromCharCode(c);
8613         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8614             return;
8615         }
8616         if(!this.maskRe.test(cc)){
8617             e.stopEvent();
8618         }
8619     },
8620      /**
8621      * Clear any invalid styles/messages for this field
8622      */
8623     clearInvalid : function(){
8624         
8625         if(!this.el || this.preventMark){ // not rendered
8626             return;
8627         }
8628         
8629         var label = this.el.select('label', true).first();
8630         var icon = this.el.select('i.fa-star', true).first();
8631         
8632         if(label && icon){
8633             icon.remove();
8634         }
8635         
8636         this.el.removeClass(this.invalidClass);
8637         
8638         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8639             
8640             var feedback = this.el.select('.form-control-feedback', true).first();
8641             
8642             if(feedback){
8643                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8644             }
8645             
8646         }
8647         
8648         this.fireEvent('valid', this);
8649     },
8650     
8651      /**
8652      * Mark this field as valid
8653      */
8654     markValid : function()
8655     {
8656         if(!this.el  || this.preventMark){ // not rendered
8657             return;
8658         }
8659         
8660         this.el.removeClass([this.invalidClass, this.validClass]);
8661         
8662         var feedback = this.el.select('.form-control-feedback', true).first();
8663             
8664         if(feedback){
8665             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8666         }
8667
8668         if(this.disabled || this.allowBlank){
8669             return;
8670         }
8671         
8672         var formGroup = this.el.findParent('.form-group', false, true);
8673         
8674         if(formGroup){
8675             
8676             var label = formGroup.select('label', true).first();
8677             var icon = formGroup.select('i.fa-star', true).first();
8678             
8679             if(label && icon){
8680                 icon.remove();
8681             }
8682         }
8683         
8684         this.el.addClass(this.validClass);
8685         
8686         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8687             
8688             var feedback = this.el.select('.form-control-feedback', true).first();
8689             
8690             if(feedback){
8691                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8692                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8693             }
8694             
8695         }
8696         
8697         this.fireEvent('valid', this);
8698     },
8699     
8700      /**
8701      * Mark this field as invalid
8702      * @param {String} msg The validation message
8703      */
8704     markInvalid : function(msg)
8705     {
8706         if(!this.el  || this.preventMark){ // not rendered
8707             return;
8708         }
8709         
8710         this.el.removeClass([this.invalidClass, this.validClass]);
8711         
8712         var feedback = this.el.select('.form-control-feedback', true).first();
8713             
8714         if(feedback){
8715             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8716         }
8717
8718         if(this.disabled || this.allowBlank){
8719             return;
8720         }
8721         
8722         var formGroup = this.el.findParent('.form-group', false, true);
8723         
8724         if(formGroup){
8725             var label = formGroup.select('label', true).first();
8726             var icon = formGroup.select('i.fa-star', true).first();
8727
8728             if(!this.getValue().length && label && !icon){
8729                 this.el.findParent('.form-group', false, true).createChild({
8730                     tag : 'i',
8731                     cls : 'text-danger fa fa-lg fa-star',
8732                     tooltip : 'This field is required',
8733                     style : 'margin-right:5px;'
8734                 }, label, true);
8735             }
8736         }
8737         
8738         
8739         this.el.addClass(this.invalidClass);
8740         
8741         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8742             
8743             var feedback = this.el.select('.form-control-feedback', true).first();
8744             
8745             if(feedback){
8746                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8747                 
8748                 if(this.getValue().length || this.forceFeedback){
8749                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8750                 }
8751                 
8752             }
8753             
8754         }
8755         
8756         this.fireEvent('invalid', this, msg);
8757     },
8758     // private
8759     SafariOnKeyDown : function(event)
8760     {
8761         // this is a workaround for a password hang bug on chrome/ webkit.
8762         
8763         var isSelectAll = false;
8764         
8765         if(this.inputEl().dom.selectionEnd > 0){
8766             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8767         }
8768         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8769             event.preventDefault();
8770             this.setValue('');
8771             return;
8772         }
8773         
8774         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8775             
8776             event.preventDefault();
8777             // this is very hacky as keydown always get's upper case.
8778             //
8779             var cc = String.fromCharCode(event.getCharCode());
8780             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8781             
8782         }
8783     },
8784     adjustWidth : function(tag, w){
8785         tag = tag.toLowerCase();
8786         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8787             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8788                 if(tag == 'input'){
8789                     return w + 2;
8790                 }
8791                 if(tag == 'textarea'){
8792                     return w-2;
8793                 }
8794             }else if(Roo.isOpera){
8795                 if(tag == 'input'){
8796                     return w + 2;
8797                 }
8798                 if(tag == 'textarea'){
8799                     return w-2;
8800                 }
8801             }
8802         }
8803         return w;
8804     }
8805     
8806 });
8807
8808  
8809 /*
8810  * - LGPL
8811  *
8812  * Input
8813  * 
8814  */
8815
8816 /**
8817  * @class Roo.bootstrap.TextArea
8818  * @extends Roo.bootstrap.Input
8819  * Bootstrap TextArea class
8820  * @cfg {Number} cols Specifies the visible width of a text area
8821  * @cfg {Number} rows Specifies the visible number of lines in a text area
8822  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8823  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8824  * @cfg {string} html text
8825  * 
8826  * @constructor
8827  * Create a new TextArea
8828  * @param {Object} config The config object
8829  */
8830
8831 Roo.bootstrap.TextArea = function(config){
8832     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8833    
8834 };
8835
8836 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8837      
8838     cols : false,
8839     rows : 5,
8840     readOnly : false,
8841     warp : 'soft',
8842     resize : false,
8843     value: false,
8844     html: false,
8845     
8846     getAutoCreate : function(){
8847         
8848         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8849         
8850         var id = Roo.id();
8851         
8852         var cfg = {};
8853         
8854         var input =  {
8855             tag: 'textarea',
8856             id : id,
8857             warp : this.warp,
8858             rows : this.rows,
8859             value : this.value || '',
8860             html: this.html || '',
8861             cls : 'form-control',
8862             placeholder : this.placeholder || '' 
8863             
8864         };
8865         
8866         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8867             input.maxLength = this.maxLength;
8868         }
8869         
8870         if(this.resize){
8871             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8872         }
8873         
8874         if(this.cols){
8875             input.cols = this.cols;
8876         }
8877         
8878         if (this.readOnly) {
8879             input.readonly = true;
8880         }
8881         
8882         if (this.name) {
8883             input.name = this.name;
8884         }
8885         
8886         if (this.size) {
8887             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8888         }
8889         
8890         var settings=this;
8891         ['xs','sm','md','lg'].map(function(size){
8892             if (settings[size]) {
8893                 cfg.cls += ' col-' + size + '-' + settings[size];
8894             }
8895         });
8896         
8897         var inputblock = input;
8898         
8899         if(this.hasFeedback && !this.allowBlank){
8900             
8901             var feedback = {
8902                 tag: 'span',
8903                 cls: 'glyphicon form-control-feedback'
8904             };
8905
8906             inputblock = {
8907                 cls : 'has-feedback',
8908                 cn :  [
8909                     input,
8910                     feedback
8911                 ] 
8912             };  
8913         }
8914         
8915         
8916         if (this.before || this.after) {
8917             
8918             inputblock = {
8919                 cls : 'input-group',
8920                 cn :  [] 
8921             };
8922             if (this.before) {
8923                 inputblock.cn.push({
8924                     tag :'span',
8925                     cls : 'input-group-addon',
8926                     html : this.before
8927                 });
8928             }
8929             
8930             inputblock.cn.push(input);
8931             
8932             if(this.hasFeedback && !this.allowBlank){
8933                 inputblock.cls += ' has-feedback';
8934                 inputblock.cn.push(feedback);
8935             }
8936             
8937             if (this.after) {
8938                 inputblock.cn.push({
8939                     tag :'span',
8940                     cls : 'input-group-addon',
8941                     html : this.after
8942                 });
8943             }
8944             
8945         }
8946         
8947         if (align ==='left' && this.fieldLabel.length) {
8948 //                Roo.log("left and has label");
8949                 cfg.cn = [
8950                     
8951                     {
8952                         tag: 'label',
8953                         'for' :  id,
8954                         cls : 'control-label col-sm-' + this.labelWidth,
8955                         html : this.fieldLabel
8956                         
8957                     },
8958                     {
8959                         cls : "col-sm-" + (12 - this.labelWidth), 
8960                         cn: [
8961                             inputblock
8962                         ]
8963                     }
8964                     
8965                 ];
8966         } else if ( this.fieldLabel.length) {
8967 //                Roo.log(" label");
8968                  cfg.cn = [
8969                    
8970                     {
8971                         tag: 'label',
8972                         //cls : 'input-group-addon',
8973                         html : this.fieldLabel
8974                         
8975                     },
8976                     
8977                     inputblock
8978                     
8979                 ];
8980
8981         } else {
8982             
8983 //                   Roo.log(" no label && no align");
8984                 cfg.cn = [
8985                     
8986                         inputblock
8987                     
8988                 ];
8989                 
8990                 
8991         }
8992         
8993         if (this.disabled) {
8994             input.disabled=true;
8995         }
8996         
8997         return cfg;
8998         
8999     },
9000     /**
9001      * return the real textarea element.
9002      */
9003     inputEl: function ()
9004     {
9005         return this.el.select('textarea.form-control',true).first();
9006     },
9007     
9008     /**
9009      * Clear any invalid styles/messages for this field
9010      */
9011     clearInvalid : function()
9012     {
9013         
9014         if(!this.el || this.preventMark){ // not rendered
9015             return;
9016         }
9017         
9018         var label = this.el.select('label', true).first();
9019         var icon = this.el.select('i.fa-star', true).first();
9020         
9021         if(label && icon){
9022             icon.remove();
9023         }
9024         
9025         this.el.removeClass(this.invalidClass);
9026         
9027         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9028             
9029             var feedback = this.el.select('.form-control-feedback', true).first();
9030             
9031             if(feedback){
9032                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9033             }
9034             
9035         }
9036         
9037         this.fireEvent('valid', this);
9038     },
9039     
9040      /**
9041      * Mark this field as valid
9042      */
9043     markValid : function()
9044     {
9045         if(!this.el  || this.preventMark){ // not rendered
9046             return;
9047         }
9048         
9049         this.el.removeClass([this.invalidClass, this.validClass]);
9050         
9051         var feedback = this.el.select('.form-control-feedback', true).first();
9052             
9053         if(feedback){
9054             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9055         }
9056
9057         if(this.disabled || this.allowBlank){
9058             return;
9059         }
9060         
9061         var label = this.el.select('label', true).first();
9062         var icon = this.el.select('i.fa-star', true).first();
9063         
9064         if(label && icon){
9065             icon.remove();
9066         }
9067         
9068         this.el.addClass(this.validClass);
9069         
9070         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9071             
9072             var feedback = this.el.select('.form-control-feedback', true).first();
9073             
9074             if(feedback){
9075                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9076                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9077             }
9078             
9079         }
9080         
9081         this.fireEvent('valid', this);
9082     },
9083     
9084      /**
9085      * Mark this field as invalid
9086      * @param {String} msg The validation message
9087      */
9088     markInvalid : function(msg)
9089     {
9090         if(!this.el  || this.preventMark){ // not rendered
9091             return;
9092         }
9093         
9094         this.el.removeClass([this.invalidClass, this.validClass]);
9095         
9096         var feedback = this.el.select('.form-control-feedback', true).first();
9097             
9098         if(feedback){
9099             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9100         }
9101
9102         if(this.disabled || this.allowBlank){
9103             return;
9104         }
9105         
9106         var label = this.el.select('label', true).first();
9107         var icon = this.el.select('i.fa-star', true).first();
9108         
9109         if(!this.getValue().length && label && !icon){
9110             this.el.createChild({
9111                 tag : 'i',
9112                 cls : 'text-danger fa fa-lg fa-star',
9113                 tooltip : 'This field is required',
9114                 style : 'margin-right:5px;'
9115             }, label, true);
9116         }
9117
9118         this.el.addClass(this.invalidClass);
9119         
9120         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9121             
9122             var feedback = this.el.select('.form-control-feedback', true).first();
9123             
9124             if(feedback){
9125                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9126                 
9127                 if(this.getValue().length || this.forceFeedback){
9128                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9129                 }
9130                 
9131             }
9132             
9133         }
9134         
9135         this.fireEvent('invalid', this, msg);
9136     }
9137 });
9138
9139  
9140 /*
9141  * - LGPL
9142  *
9143  * trigger field - base class for combo..
9144  * 
9145  */
9146  
9147 /**
9148  * @class Roo.bootstrap.TriggerField
9149  * @extends Roo.bootstrap.Input
9150  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9151  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9152  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9153  * for which you can provide a custom implementation.  For example:
9154  * <pre><code>
9155 var trigger = new Roo.bootstrap.TriggerField();
9156 trigger.onTriggerClick = myTriggerFn;
9157 trigger.applyTo('my-field');
9158 </code></pre>
9159  *
9160  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9161  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9162  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9163  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9164  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9165
9166  * @constructor
9167  * Create a new TriggerField.
9168  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9169  * to the base TextField)
9170  */
9171 Roo.bootstrap.TriggerField = function(config){
9172     this.mimicing = false;
9173     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9174 };
9175
9176 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9177     /**
9178      * @cfg {String} triggerClass A CSS class to apply to the trigger
9179      */
9180      /**
9181      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9182      */
9183     hideTrigger:false,
9184
9185     /**
9186      * @cfg {Boolean} removable (true|false) special filter default false
9187      */
9188     removable : false,
9189     
9190     /** @cfg {Boolean} grow @hide */
9191     /** @cfg {Number} growMin @hide */
9192     /** @cfg {Number} growMax @hide */
9193
9194     /**
9195      * @hide 
9196      * @method
9197      */
9198     autoSize: Roo.emptyFn,
9199     // private
9200     monitorTab : true,
9201     // private
9202     deferHeight : true,
9203
9204     
9205     actionMode : 'wrap',
9206     
9207     caret : false,
9208     
9209     
9210     getAutoCreate : function(){
9211        
9212         var align = this.labelAlign || this.parentLabelAlign();
9213         
9214         var id = Roo.id();
9215         
9216         var cfg = {
9217             cls: 'form-group' //input-group
9218         };
9219         
9220         
9221         var input =  {
9222             tag: 'input',
9223             id : id,
9224             type : this.inputType,
9225             cls : 'form-control',
9226             autocomplete: 'new-password',
9227             placeholder : this.placeholder || '' 
9228             
9229         };
9230         if (this.name) {
9231             input.name = this.name;
9232         }
9233         if (this.size) {
9234             input.cls += ' input-' + this.size;
9235         }
9236         
9237         if (this.disabled) {
9238             input.disabled=true;
9239         }
9240         
9241         var inputblock = input;
9242         
9243         if(this.hasFeedback && !this.allowBlank){
9244             
9245             var feedback = {
9246                 tag: 'span',
9247                 cls: 'glyphicon form-control-feedback'
9248             };
9249             
9250             if(this.removable && !this.editable && !this.tickable){
9251                 inputblock = {
9252                     cls : 'has-feedback',
9253                     cn :  [
9254                         inputblock,
9255                         {
9256                             tag: 'button',
9257                             html : 'x',
9258                             cls : 'roo-combo-removable-btn close'
9259                         },
9260                         feedback
9261                     ] 
9262                 };
9263             } else {
9264                 inputblock = {
9265                     cls : 'has-feedback',
9266                     cn :  [
9267                         inputblock,
9268                         feedback
9269                     ] 
9270                 };
9271             }
9272
9273         } else {
9274             if(this.removable && !this.editable && !this.tickable){
9275                 inputblock = {
9276                     cls : 'roo-removable',
9277                     cn :  [
9278                         inputblock,
9279                         {
9280                             tag: 'button',
9281                             html : 'x',
9282                             cls : 'roo-combo-removable-btn close'
9283                         }
9284                     ] 
9285                 };
9286             }
9287         }
9288         
9289         if (this.before || this.after) {
9290             
9291             inputblock = {
9292                 cls : 'input-group',
9293                 cn :  [] 
9294             };
9295             if (this.before) {
9296                 inputblock.cn.push({
9297                     tag :'span',
9298                     cls : 'input-group-addon',
9299                     html : this.before
9300                 });
9301             }
9302             
9303             inputblock.cn.push(input);
9304             
9305             if(this.hasFeedback && !this.allowBlank){
9306                 inputblock.cls += ' has-feedback';
9307                 inputblock.cn.push(feedback);
9308             }
9309             
9310             if (this.after) {
9311                 inputblock.cn.push({
9312                     tag :'span',
9313                     cls : 'input-group-addon',
9314                     html : this.after
9315                 });
9316             }
9317             
9318         };
9319         
9320         var box = {
9321             tag: 'div',
9322             cn: [
9323                 {
9324                     tag: 'input',
9325                     type : 'hidden',
9326                     cls: 'form-hidden-field'
9327                 },
9328                 inputblock
9329             ]
9330             
9331         };
9332         
9333         if(this.multiple){
9334             box = {
9335                 tag: 'div',
9336                 cn: [
9337                     {
9338                         tag: 'input',
9339                         type : 'hidden',
9340                         cls: 'form-hidden-field'
9341                     },
9342                     {
9343                         tag: 'ul',
9344                         cls: 'roo-select2-choices',
9345                         cn:[
9346                             {
9347                                 tag: 'li',
9348                                 cls: 'roo-select2-search-field',
9349                                 cn: [
9350
9351                                     inputblock
9352                                 ]
9353                             }
9354                         ]
9355                     }
9356                 ]
9357             }
9358         };
9359         
9360         var combobox = {
9361             cls: 'roo-select2-container input-group',
9362             cn: [
9363                 box
9364 //                {
9365 //                    tag: 'ul',
9366 //                    cls: 'typeahead typeahead-long dropdown-menu',
9367 //                    style: 'display:none'
9368 //                }
9369             ]
9370         };
9371         
9372         if(!this.multiple && this.showToggleBtn){
9373             
9374             var caret = {
9375                         tag: 'span',
9376                         cls: 'caret'
9377              };
9378             if (this.caret != false) {
9379                 caret = {
9380                      tag: 'i',
9381                      cls: 'fa fa-' + this.caret
9382                 };
9383                 
9384             }
9385             
9386             combobox.cn.push({
9387                 tag :'span',
9388                 cls : 'input-group-addon btn dropdown-toggle',
9389                 cn : [
9390                     caret,
9391                     {
9392                         tag: 'span',
9393                         cls: 'combobox-clear',
9394                         cn  : [
9395                             {
9396                                 tag : 'i',
9397                                 cls: 'icon-remove'
9398                             }
9399                         ]
9400                     }
9401                 ]
9402
9403             })
9404         }
9405         
9406         if(this.multiple){
9407             combobox.cls += ' roo-select2-container-multi';
9408         }
9409         
9410         if (align ==='left' && this.fieldLabel.length) {
9411             
9412 //                Roo.log("left and has label");
9413                 cfg.cn = [
9414                     
9415                     {
9416                         tag: 'label',
9417                         'for' :  id,
9418                         cls : 'control-label col-sm-' + this.labelWidth,
9419                         html : this.fieldLabel
9420                         
9421                     },
9422                     {
9423                         cls : "col-sm-" + (12 - this.labelWidth), 
9424                         cn: [
9425                             combobox
9426                         ]
9427                     }
9428                     
9429                 ];
9430         } else if ( this.fieldLabel.length) {
9431 //                Roo.log(" label");
9432                  cfg.cn = [
9433                    
9434                     {
9435                         tag: 'label',
9436                         //cls : 'input-group-addon',
9437                         html : this.fieldLabel
9438                         
9439                     },
9440                     
9441                     combobox
9442                     
9443                 ];
9444
9445         } else {
9446             
9447 //                Roo.log(" no label && no align");
9448                 cfg = combobox
9449                      
9450                 
9451         }
9452          
9453         var settings=this;
9454         ['xs','sm','md','lg'].map(function(size){
9455             if (settings[size]) {
9456                 cfg.cls += ' col-' + size + '-' + settings[size];
9457             }
9458         });
9459         
9460         return cfg;
9461         
9462     },
9463     
9464     
9465     
9466     // private
9467     onResize : function(w, h){
9468 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9469 //        if(typeof w == 'number'){
9470 //            var x = w - this.trigger.getWidth();
9471 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9472 //            this.trigger.setStyle('left', x+'px');
9473 //        }
9474     },
9475
9476     // private
9477     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9478
9479     // private
9480     getResizeEl : function(){
9481         return this.inputEl();
9482     },
9483
9484     // private
9485     getPositionEl : function(){
9486         return this.inputEl();
9487     },
9488
9489     // private
9490     alignErrorIcon : function(){
9491         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9492     },
9493
9494     // private
9495     initEvents : function(){
9496         
9497         this.createList();
9498         
9499         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9500         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9501         if(!this.multiple && this.showToggleBtn){
9502             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9503             if(this.hideTrigger){
9504                 this.trigger.setDisplayed(false);
9505             }
9506             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9507         }
9508         
9509         if(this.multiple){
9510             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9511         }
9512         
9513         if(this.removable && !this.editable && !this.tickable){
9514             var close = this.closeTriggerEl();
9515             
9516             if(close){
9517                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9518                 close.on('click', this.removeBtnClick, this, close);
9519             }
9520         }
9521         
9522         //this.trigger.addClassOnOver('x-form-trigger-over');
9523         //this.trigger.addClassOnClick('x-form-trigger-click');
9524         
9525         //if(!this.width){
9526         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9527         //}
9528     },
9529     
9530     closeTriggerEl : function()
9531     {
9532         var close = this.el.select('.roo-combo-removable-btn', true).first();
9533         return close ? close : false;
9534     },
9535     
9536     removeBtnClick : function(e, h, el)
9537     {
9538         e.preventDefault();
9539         
9540         if(this.fireEvent("remove", this) !== false){
9541             this.reset();
9542             this.fireEvent("afterremove", this)
9543         }
9544     },
9545     
9546     createList : function()
9547     {
9548         this.list = Roo.get(document.body).createChild({
9549             tag: 'ul',
9550             cls: 'typeahead typeahead-long dropdown-menu',
9551             style: 'display:none'
9552         });
9553         
9554         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9555         
9556     },
9557
9558     // private
9559     initTrigger : function(){
9560        
9561     },
9562
9563     // private
9564     onDestroy : function(){
9565         if(this.trigger){
9566             this.trigger.removeAllListeners();
9567           //  this.trigger.remove();
9568         }
9569         //if(this.wrap){
9570         //    this.wrap.remove();
9571         //}
9572         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9573     },
9574
9575     // private
9576     onFocus : function(){
9577         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9578         /*
9579         if(!this.mimicing){
9580             this.wrap.addClass('x-trigger-wrap-focus');
9581             this.mimicing = true;
9582             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9583             if(this.monitorTab){
9584                 this.el.on("keydown", this.checkTab, this);
9585             }
9586         }
9587         */
9588     },
9589
9590     // private
9591     checkTab : function(e){
9592         if(e.getKey() == e.TAB){
9593             this.triggerBlur();
9594         }
9595     },
9596
9597     // private
9598     onBlur : function(){
9599         // do nothing
9600     },
9601
9602     // private
9603     mimicBlur : function(e, t){
9604         /*
9605         if(!this.wrap.contains(t) && this.validateBlur()){
9606             this.triggerBlur();
9607         }
9608         */
9609     },
9610
9611     // private
9612     triggerBlur : function(){
9613         this.mimicing = false;
9614         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9615         if(this.monitorTab){
9616             this.el.un("keydown", this.checkTab, this);
9617         }
9618         //this.wrap.removeClass('x-trigger-wrap-focus');
9619         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9620     },
9621
9622     // private
9623     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9624     validateBlur : function(e, t){
9625         return true;
9626     },
9627
9628     // private
9629     onDisable : function(){
9630         this.inputEl().dom.disabled = true;
9631         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9632         //if(this.wrap){
9633         //    this.wrap.addClass('x-item-disabled');
9634         //}
9635     },
9636
9637     // private
9638     onEnable : function(){
9639         this.inputEl().dom.disabled = false;
9640         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9641         //if(this.wrap){
9642         //    this.el.removeClass('x-item-disabled');
9643         //}
9644     },
9645
9646     // private
9647     onShow : function(){
9648         var ae = this.getActionEl();
9649         
9650         if(ae){
9651             ae.dom.style.display = '';
9652             ae.dom.style.visibility = 'visible';
9653         }
9654     },
9655
9656     // private
9657     
9658     onHide : function(){
9659         var ae = this.getActionEl();
9660         ae.dom.style.display = 'none';
9661     },
9662
9663     /**
9664      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9665      * by an implementing function.
9666      * @method
9667      * @param {EventObject} e
9668      */
9669     onTriggerClick : Roo.emptyFn
9670 });
9671  /*
9672  * Based on:
9673  * Ext JS Library 1.1.1
9674  * Copyright(c) 2006-2007, Ext JS, LLC.
9675  *
9676  * Originally Released Under LGPL - original licence link has changed is not relivant.
9677  *
9678  * Fork - LGPL
9679  * <script type="text/javascript">
9680  */
9681
9682
9683 /**
9684  * @class Roo.data.SortTypes
9685  * @singleton
9686  * Defines the default sorting (casting?) comparison functions used when sorting data.
9687  */
9688 Roo.data.SortTypes = {
9689     /**
9690      * Default sort that does nothing
9691      * @param {Mixed} s The value being converted
9692      * @return {Mixed} The comparison value
9693      */
9694     none : function(s){
9695         return s;
9696     },
9697     
9698     /**
9699      * The regular expression used to strip tags
9700      * @type {RegExp}
9701      * @property
9702      */
9703     stripTagsRE : /<\/?[^>]+>/gi,
9704     
9705     /**
9706      * Strips all HTML tags to sort on text only
9707      * @param {Mixed} s The value being converted
9708      * @return {String} The comparison value
9709      */
9710     asText : function(s){
9711         return String(s).replace(this.stripTagsRE, "");
9712     },
9713     
9714     /**
9715      * Strips all HTML tags to sort on text only - Case insensitive
9716      * @param {Mixed} s The value being converted
9717      * @return {String} The comparison value
9718      */
9719     asUCText : function(s){
9720         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9721     },
9722     
9723     /**
9724      * Case insensitive string
9725      * @param {Mixed} s The value being converted
9726      * @return {String} The comparison value
9727      */
9728     asUCString : function(s) {
9729         return String(s).toUpperCase();
9730     },
9731     
9732     /**
9733      * Date sorting
9734      * @param {Mixed} s The value being converted
9735      * @return {Number} The comparison value
9736      */
9737     asDate : function(s) {
9738         if(!s){
9739             return 0;
9740         }
9741         if(s instanceof Date){
9742             return s.getTime();
9743         }
9744         return Date.parse(String(s));
9745     },
9746     
9747     /**
9748      * Float sorting
9749      * @param {Mixed} s The value being converted
9750      * @return {Float} The comparison value
9751      */
9752     asFloat : function(s) {
9753         var val = parseFloat(String(s).replace(/,/g, ""));
9754         if(isNaN(val)) {
9755             val = 0;
9756         }
9757         return val;
9758     },
9759     
9760     /**
9761      * Integer sorting
9762      * @param {Mixed} s The value being converted
9763      * @return {Number} The comparison value
9764      */
9765     asInt : function(s) {
9766         var val = parseInt(String(s).replace(/,/g, ""));
9767         if(isNaN(val)) {
9768             val = 0;
9769         }
9770         return val;
9771     }
9772 };/*
9773  * Based on:
9774  * Ext JS Library 1.1.1
9775  * Copyright(c) 2006-2007, Ext JS, LLC.
9776  *
9777  * Originally Released Under LGPL - original licence link has changed is not relivant.
9778  *
9779  * Fork - LGPL
9780  * <script type="text/javascript">
9781  */
9782
9783 /**
9784 * @class Roo.data.Record
9785  * Instances of this class encapsulate both record <em>definition</em> information, and record
9786  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9787  * to access Records cached in an {@link Roo.data.Store} object.<br>
9788  * <p>
9789  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9790  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9791  * objects.<br>
9792  * <p>
9793  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9794  * @constructor
9795  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9796  * {@link #create}. The parameters are the same.
9797  * @param {Array} data An associative Array of data values keyed by the field name.
9798  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9799  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9800  * not specified an integer id is generated.
9801  */
9802 Roo.data.Record = function(data, id){
9803     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9804     this.data = data;
9805 };
9806
9807 /**
9808  * Generate a constructor for a specific record layout.
9809  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9810  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9811  * Each field definition object may contain the following properties: <ul>
9812  * <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,
9813  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9814  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9815  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9816  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9817  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9818  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9819  * this may be omitted.</p></li>
9820  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9821  * <ul><li>auto (Default, implies no conversion)</li>
9822  * <li>string</li>
9823  * <li>int</li>
9824  * <li>float</li>
9825  * <li>boolean</li>
9826  * <li>date</li></ul></p></li>
9827  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9828  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9829  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9830  * by the Reader into an object that will be stored in the Record. It is passed the
9831  * following parameters:<ul>
9832  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9833  * </ul></p></li>
9834  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9835  * </ul>
9836  * <br>usage:<br><pre><code>
9837 var TopicRecord = Roo.data.Record.create(
9838     {name: 'title', mapping: 'topic_title'},
9839     {name: 'author', mapping: 'username'},
9840     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9841     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9842     {name: 'lastPoster', mapping: 'user2'},
9843     {name: 'excerpt', mapping: 'post_text'}
9844 );
9845
9846 var myNewRecord = new TopicRecord({
9847     title: 'Do my job please',
9848     author: 'noobie',
9849     totalPosts: 1,
9850     lastPost: new Date(),
9851     lastPoster: 'Animal',
9852     excerpt: 'No way dude!'
9853 });
9854 myStore.add(myNewRecord);
9855 </code></pre>
9856  * @method create
9857  * @static
9858  */
9859 Roo.data.Record.create = function(o){
9860     var f = function(){
9861         f.superclass.constructor.apply(this, arguments);
9862     };
9863     Roo.extend(f, Roo.data.Record);
9864     var p = f.prototype;
9865     p.fields = new Roo.util.MixedCollection(false, function(field){
9866         return field.name;
9867     });
9868     for(var i = 0, len = o.length; i < len; i++){
9869         p.fields.add(new Roo.data.Field(o[i]));
9870     }
9871     f.getField = function(name){
9872         return p.fields.get(name);  
9873     };
9874     return f;
9875 };
9876
9877 Roo.data.Record.AUTO_ID = 1000;
9878 Roo.data.Record.EDIT = 'edit';
9879 Roo.data.Record.REJECT = 'reject';
9880 Roo.data.Record.COMMIT = 'commit';
9881
9882 Roo.data.Record.prototype = {
9883     /**
9884      * Readonly flag - true if this record has been modified.
9885      * @type Boolean
9886      */
9887     dirty : false,
9888     editing : false,
9889     error: null,
9890     modified: null,
9891
9892     // private
9893     join : function(store){
9894         this.store = store;
9895     },
9896
9897     /**
9898      * Set the named field to the specified value.
9899      * @param {String} name The name of the field to set.
9900      * @param {Object} value The value to set the field to.
9901      */
9902     set : function(name, value){
9903         if(this.data[name] == value){
9904             return;
9905         }
9906         this.dirty = true;
9907         if(!this.modified){
9908             this.modified = {};
9909         }
9910         if(typeof this.modified[name] == 'undefined'){
9911             this.modified[name] = this.data[name];
9912         }
9913         this.data[name] = value;
9914         if(!this.editing && this.store){
9915             this.store.afterEdit(this);
9916         }       
9917     },
9918
9919     /**
9920      * Get the value of the named field.
9921      * @param {String} name The name of the field to get the value of.
9922      * @return {Object} The value of the field.
9923      */
9924     get : function(name){
9925         return this.data[name]; 
9926     },
9927
9928     // private
9929     beginEdit : function(){
9930         this.editing = true;
9931         this.modified = {}; 
9932     },
9933
9934     // private
9935     cancelEdit : function(){
9936         this.editing = false;
9937         delete this.modified;
9938     },
9939
9940     // private
9941     endEdit : function(){
9942         this.editing = false;
9943         if(this.dirty && this.store){
9944             this.store.afterEdit(this);
9945         }
9946     },
9947
9948     /**
9949      * Usually called by the {@link Roo.data.Store} which owns the Record.
9950      * Rejects all changes made to the Record since either creation, or the last commit operation.
9951      * Modified fields are reverted to their original values.
9952      * <p>
9953      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9954      * of reject operations.
9955      */
9956     reject : function(){
9957         var m = this.modified;
9958         for(var n in m){
9959             if(typeof m[n] != "function"){
9960                 this.data[n] = m[n];
9961             }
9962         }
9963         this.dirty = false;
9964         delete this.modified;
9965         this.editing = false;
9966         if(this.store){
9967             this.store.afterReject(this);
9968         }
9969     },
9970
9971     /**
9972      * Usually called by the {@link Roo.data.Store} which owns the Record.
9973      * Commits all changes made to the Record since either creation, or the last commit operation.
9974      * <p>
9975      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9976      * of commit operations.
9977      */
9978     commit : function(){
9979         this.dirty = false;
9980         delete this.modified;
9981         this.editing = false;
9982         if(this.store){
9983             this.store.afterCommit(this);
9984         }
9985     },
9986
9987     // private
9988     hasError : function(){
9989         return this.error != null;
9990     },
9991
9992     // private
9993     clearError : function(){
9994         this.error = null;
9995     },
9996
9997     /**
9998      * Creates a copy of this record.
9999      * @param {String} id (optional) A new record id if you don't want to use this record's id
10000      * @return {Record}
10001      */
10002     copy : function(newId) {
10003         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10004     }
10005 };/*
10006  * Based on:
10007  * Ext JS Library 1.1.1
10008  * Copyright(c) 2006-2007, Ext JS, LLC.
10009  *
10010  * Originally Released Under LGPL - original licence link has changed is not relivant.
10011  *
10012  * Fork - LGPL
10013  * <script type="text/javascript">
10014  */
10015
10016
10017
10018 /**
10019  * @class Roo.data.Store
10020  * @extends Roo.util.Observable
10021  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10022  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10023  * <p>
10024  * 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
10025  * has no knowledge of the format of the data returned by the Proxy.<br>
10026  * <p>
10027  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10028  * instances from the data object. These records are cached and made available through accessor functions.
10029  * @constructor
10030  * Creates a new Store.
10031  * @param {Object} config A config object containing the objects needed for the Store to access data,
10032  * and read the data into Records.
10033  */
10034 Roo.data.Store = function(config){
10035     this.data = new Roo.util.MixedCollection(false);
10036     this.data.getKey = function(o){
10037         return o.id;
10038     };
10039     this.baseParams = {};
10040     // private
10041     this.paramNames = {
10042         "start" : "start",
10043         "limit" : "limit",
10044         "sort" : "sort",
10045         "dir" : "dir",
10046         "multisort" : "_multisort"
10047     };
10048
10049     if(config && config.data){
10050         this.inlineData = config.data;
10051         delete config.data;
10052     }
10053
10054     Roo.apply(this, config);
10055     
10056     if(this.reader){ // reader passed
10057         this.reader = Roo.factory(this.reader, Roo.data);
10058         this.reader.xmodule = this.xmodule || false;
10059         if(!this.recordType){
10060             this.recordType = this.reader.recordType;
10061         }
10062         if(this.reader.onMetaChange){
10063             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10064         }
10065     }
10066
10067     if(this.recordType){
10068         this.fields = this.recordType.prototype.fields;
10069     }
10070     this.modified = [];
10071
10072     this.addEvents({
10073         /**
10074          * @event datachanged
10075          * Fires when the data cache has changed, and a widget which is using this Store
10076          * as a Record cache should refresh its view.
10077          * @param {Store} this
10078          */
10079         datachanged : true,
10080         /**
10081          * @event metachange
10082          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10083          * @param {Store} this
10084          * @param {Object} meta The JSON metadata
10085          */
10086         metachange : true,
10087         /**
10088          * @event add
10089          * Fires when Records have been added to the Store
10090          * @param {Store} this
10091          * @param {Roo.data.Record[]} records The array of Records added
10092          * @param {Number} index The index at which the record(s) were added
10093          */
10094         add : true,
10095         /**
10096          * @event remove
10097          * Fires when a Record has been removed from the Store
10098          * @param {Store} this
10099          * @param {Roo.data.Record} record The Record that was removed
10100          * @param {Number} index The index at which the record was removed
10101          */
10102         remove : true,
10103         /**
10104          * @event update
10105          * Fires when a Record has been updated
10106          * @param {Store} this
10107          * @param {Roo.data.Record} record The Record that was updated
10108          * @param {String} operation The update operation being performed.  Value may be one of:
10109          * <pre><code>
10110  Roo.data.Record.EDIT
10111  Roo.data.Record.REJECT
10112  Roo.data.Record.COMMIT
10113          * </code></pre>
10114          */
10115         update : true,
10116         /**
10117          * @event clear
10118          * Fires when the data cache has been cleared.
10119          * @param {Store} this
10120          */
10121         clear : true,
10122         /**
10123          * @event beforeload
10124          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10125          * the load action will be canceled.
10126          * @param {Store} this
10127          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10128          */
10129         beforeload : true,
10130         /**
10131          * @event beforeloadadd
10132          * Fires after a new set of Records has been loaded.
10133          * @param {Store} this
10134          * @param {Roo.data.Record[]} records The Records that were loaded
10135          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10136          */
10137         beforeloadadd : true,
10138         /**
10139          * @event load
10140          * Fires after a new set of Records has been loaded, before they are added to the store.
10141          * @param {Store} this
10142          * @param {Roo.data.Record[]} records The Records that were loaded
10143          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10144          * @params {Object} return from reader
10145          */
10146         load : true,
10147         /**
10148          * @event loadexception
10149          * Fires if an exception occurs in the Proxy during loading.
10150          * Called with the signature of the Proxy's "loadexception" event.
10151          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10152          * 
10153          * @param {Proxy} 
10154          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10155          * @param {Object} load options 
10156          * @param {Object} jsonData from your request (normally this contains the Exception)
10157          */
10158         loadexception : true
10159     });
10160     
10161     if(this.proxy){
10162         this.proxy = Roo.factory(this.proxy, Roo.data);
10163         this.proxy.xmodule = this.xmodule || false;
10164         this.relayEvents(this.proxy,  ["loadexception"]);
10165     }
10166     this.sortToggle = {};
10167     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10168
10169     Roo.data.Store.superclass.constructor.call(this);
10170
10171     if(this.inlineData){
10172         this.loadData(this.inlineData);
10173         delete this.inlineData;
10174     }
10175 };
10176
10177 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10178      /**
10179     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10180     * without a remote query - used by combo/forms at present.
10181     */
10182     
10183     /**
10184     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10185     */
10186     /**
10187     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10188     */
10189     /**
10190     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10191     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10192     */
10193     /**
10194     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10195     * on any HTTP request
10196     */
10197     /**
10198     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10199     */
10200     /**
10201     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10202     */
10203     multiSort: false,
10204     /**
10205     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10206     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10207     */
10208     remoteSort : false,
10209
10210     /**
10211     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10212      * loaded or when a record is removed. (defaults to false).
10213     */
10214     pruneModifiedRecords : false,
10215
10216     // private
10217     lastOptions : null,
10218
10219     /**
10220      * Add Records to the Store and fires the add event.
10221      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10222      */
10223     add : function(records){
10224         records = [].concat(records);
10225         for(var i = 0, len = records.length; i < len; i++){
10226             records[i].join(this);
10227         }
10228         var index = this.data.length;
10229         this.data.addAll(records);
10230         this.fireEvent("add", this, records, index);
10231     },
10232
10233     /**
10234      * Remove a Record from the Store and fires the remove event.
10235      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10236      */
10237     remove : function(record){
10238         var index = this.data.indexOf(record);
10239         this.data.removeAt(index);
10240         if(this.pruneModifiedRecords){
10241             this.modified.remove(record);
10242         }
10243         this.fireEvent("remove", this, record, index);
10244     },
10245
10246     /**
10247      * Remove all Records from the Store and fires the clear event.
10248      */
10249     removeAll : function(){
10250         this.data.clear();
10251         if(this.pruneModifiedRecords){
10252             this.modified = [];
10253         }
10254         this.fireEvent("clear", this);
10255     },
10256
10257     /**
10258      * Inserts Records to the Store at the given index and fires the add event.
10259      * @param {Number} index The start index at which to insert the passed Records.
10260      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10261      */
10262     insert : function(index, records){
10263         records = [].concat(records);
10264         for(var i = 0, len = records.length; i < len; i++){
10265             this.data.insert(index, records[i]);
10266             records[i].join(this);
10267         }
10268         this.fireEvent("add", this, records, index);
10269     },
10270
10271     /**
10272      * Get the index within the cache of the passed Record.
10273      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10274      * @return {Number} The index of the passed Record. Returns -1 if not found.
10275      */
10276     indexOf : function(record){
10277         return this.data.indexOf(record);
10278     },
10279
10280     /**
10281      * Get the index within the cache of the Record with the passed id.
10282      * @param {String} id The id of the Record to find.
10283      * @return {Number} The index of the Record. Returns -1 if not found.
10284      */
10285     indexOfId : function(id){
10286         return this.data.indexOfKey(id);
10287     },
10288
10289     /**
10290      * Get the Record with the specified id.
10291      * @param {String} id The id of the Record to find.
10292      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10293      */
10294     getById : function(id){
10295         return this.data.key(id);
10296     },
10297
10298     /**
10299      * Get the Record at the specified index.
10300      * @param {Number} index The index of the Record to find.
10301      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10302      */
10303     getAt : function(index){
10304         return this.data.itemAt(index);
10305     },
10306
10307     /**
10308      * Returns a range of Records between specified indices.
10309      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10310      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10311      * @return {Roo.data.Record[]} An array of Records
10312      */
10313     getRange : function(start, end){
10314         return this.data.getRange(start, end);
10315     },
10316
10317     // private
10318     storeOptions : function(o){
10319         o = Roo.apply({}, o);
10320         delete o.callback;
10321         delete o.scope;
10322         this.lastOptions = o;
10323     },
10324
10325     /**
10326      * Loads the Record cache from the configured Proxy using the configured Reader.
10327      * <p>
10328      * If using remote paging, then the first load call must specify the <em>start</em>
10329      * and <em>limit</em> properties in the options.params property to establish the initial
10330      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10331      * <p>
10332      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10333      * and this call will return before the new data has been loaded. Perform any post-processing
10334      * in a callback function, or in a "load" event handler.</strong>
10335      * <p>
10336      * @param {Object} options An object containing properties which control loading options:<ul>
10337      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10338      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10339      * passed the following arguments:<ul>
10340      * <li>r : Roo.data.Record[]</li>
10341      * <li>options: Options object from the load call</li>
10342      * <li>success: Boolean success indicator</li></ul></li>
10343      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10344      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10345      * </ul>
10346      */
10347     load : function(options){
10348         options = options || {};
10349         if(this.fireEvent("beforeload", this, options) !== false){
10350             this.storeOptions(options);
10351             var p = Roo.apply(options.params || {}, this.baseParams);
10352             // if meta was not loaded from remote source.. try requesting it.
10353             if (!this.reader.metaFromRemote) {
10354                 p._requestMeta = 1;
10355             }
10356             if(this.sortInfo && this.remoteSort){
10357                 var pn = this.paramNames;
10358                 p[pn["sort"]] = this.sortInfo.field;
10359                 p[pn["dir"]] = this.sortInfo.direction;
10360             }
10361             if (this.multiSort) {
10362                 var pn = this.paramNames;
10363                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10364             }
10365             
10366             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10367         }
10368     },
10369
10370     /**
10371      * Reloads the Record cache from the configured Proxy using the configured Reader and
10372      * the options from the last load operation performed.
10373      * @param {Object} options (optional) An object containing properties which may override the options
10374      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10375      * the most recently used options are reused).
10376      */
10377     reload : function(options){
10378         this.load(Roo.applyIf(options||{}, this.lastOptions));
10379     },
10380
10381     // private
10382     // Called as a callback by the Reader during a load operation.
10383     loadRecords : function(o, options, success){
10384         if(!o || success === false){
10385             if(success !== false){
10386                 this.fireEvent("load", this, [], options, o);
10387             }
10388             if(options.callback){
10389                 options.callback.call(options.scope || this, [], options, false);
10390             }
10391             return;
10392         }
10393         // if data returned failure - throw an exception.
10394         if (o.success === false) {
10395             // show a message if no listener is registered.
10396             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10397                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10398             }
10399             // loadmask wil be hooked into this..
10400             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10401             return;
10402         }
10403         var r = o.records, t = o.totalRecords || r.length;
10404         
10405         this.fireEvent("beforeloadadd", this, r, options, o);
10406         
10407         if(!options || options.add !== true){
10408             if(this.pruneModifiedRecords){
10409                 this.modified = [];
10410             }
10411             for(var i = 0, len = r.length; i < len; i++){
10412                 r[i].join(this);
10413             }
10414             if(this.snapshot){
10415                 this.data = this.snapshot;
10416                 delete this.snapshot;
10417             }
10418             this.data.clear();
10419             this.data.addAll(r);
10420             this.totalLength = t;
10421             this.applySort();
10422             this.fireEvent("datachanged", this);
10423         }else{
10424             this.totalLength = Math.max(t, this.data.length+r.length);
10425             this.add(r);
10426         }
10427         this.fireEvent("load", this, r, options, o);
10428         if(options.callback){
10429             options.callback.call(options.scope || this, r, options, true);
10430         }
10431     },
10432
10433
10434     /**
10435      * Loads data from a passed data block. A Reader which understands the format of the data
10436      * must have been configured in the constructor.
10437      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10438      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10439      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10440      */
10441     loadData : function(o, append){
10442         var r = this.reader.readRecords(o);
10443         this.loadRecords(r, {add: append}, true);
10444     },
10445
10446     /**
10447      * Gets the number of cached records.
10448      * <p>
10449      * <em>If using paging, this may not be the total size of the dataset. If the data object
10450      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10451      * the data set size</em>
10452      */
10453     getCount : function(){
10454         return this.data.length || 0;
10455     },
10456
10457     /**
10458      * Gets the total number of records in the dataset as returned by the server.
10459      * <p>
10460      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10461      * the dataset size</em>
10462      */
10463     getTotalCount : function(){
10464         return this.totalLength || 0;
10465     },
10466
10467     /**
10468      * Returns the sort state of the Store as an object with two properties:
10469      * <pre><code>
10470  field {String} The name of the field by which the Records are sorted
10471  direction {String} The sort order, "ASC" or "DESC"
10472      * </code></pre>
10473      */
10474     getSortState : function(){
10475         return this.sortInfo;
10476     },
10477
10478     // private
10479     applySort : function(){
10480         if(this.sortInfo && !this.remoteSort){
10481             var s = this.sortInfo, f = s.field;
10482             var st = this.fields.get(f).sortType;
10483             var fn = function(r1, r2){
10484                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10485                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10486             };
10487             this.data.sort(s.direction, fn);
10488             if(this.snapshot && this.snapshot != this.data){
10489                 this.snapshot.sort(s.direction, fn);
10490             }
10491         }
10492     },
10493
10494     /**
10495      * Sets the default sort column and order to be used by the next load operation.
10496      * @param {String} fieldName The name of the field to sort by.
10497      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10498      */
10499     setDefaultSort : function(field, dir){
10500         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10501     },
10502
10503     /**
10504      * Sort the Records.
10505      * If remote sorting is used, the sort is performed on the server, and the cache is
10506      * reloaded. If local sorting is used, the cache is sorted internally.
10507      * @param {String} fieldName The name of the field to sort by.
10508      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10509      */
10510     sort : function(fieldName, dir){
10511         var f = this.fields.get(fieldName);
10512         if(!dir){
10513             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10514             
10515             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10516                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10517             }else{
10518                 dir = f.sortDir;
10519             }
10520         }
10521         this.sortToggle[f.name] = dir;
10522         this.sortInfo = {field: f.name, direction: dir};
10523         if(!this.remoteSort){
10524             this.applySort();
10525             this.fireEvent("datachanged", this);
10526         }else{
10527             this.load(this.lastOptions);
10528         }
10529     },
10530
10531     /**
10532      * Calls the specified function for each of the Records in the cache.
10533      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10534      * Returning <em>false</em> aborts and exits the iteration.
10535      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10536      */
10537     each : function(fn, scope){
10538         this.data.each(fn, scope);
10539     },
10540
10541     /**
10542      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10543      * (e.g., during paging).
10544      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10545      */
10546     getModifiedRecords : function(){
10547         return this.modified;
10548     },
10549
10550     // private
10551     createFilterFn : function(property, value, anyMatch){
10552         if(!value.exec){ // not a regex
10553             value = String(value);
10554             if(value.length == 0){
10555                 return false;
10556             }
10557             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10558         }
10559         return function(r){
10560             return value.test(r.data[property]);
10561         };
10562     },
10563
10564     /**
10565      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10566      * @param {String} property A field on your records
10567      * @param {Number} start The record index to start at (defaults to 0)
10568      * @param {Number} end The last record index to include (defaults to length - 1)
10569      * @return {Number} The sum
10570      */
10571     sum : function(property, start, end){
10572         var rs = this.data.items, v = 0;
10573         start = start || 0;
10574         end = (end || end === 0) ? end : rs.length-1;
10575
10576         for(var i = start; i <= end; i++){
10577             v += (rs[i].data[property] || 0);
10578         }
10579         return v;
10580     },
10581
10582     /**
10583      * Filter the records by a specified property.
10584      * @param {String} field A field on your records
10585      * @param {String/RegExp} value Either a string that the field
10586      * should start with or a RegExp to test against the field
10587      * @param {Boolean} anyMatch True to match any part not just the beginning
10588      */
10589     filter : function(property, value, anyMatch){
10590         var fn = this.createFilterFn(property, value, anyMatch);
10591         return fn ? this.filterBy(fn) : this.clearFilter();
10592     },
10593
10594     /**
10595      * Filter by a function. The specified function will be called with each
10596      * record in this data source. If the function returns true the record is included,
10597      * otherwise it is filtered.
10598      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10599      * @param {Object} scope (optional) The scope of the function (defaults to this)
10600      */
10601     filterBy : function(fn, scope){
10602         this.snapshot = this.snapshot || this.data;
10603         this.data = this.queryBy(fn, scope||this);
10604         this.fireEvent("datachanged", this);
10605     },
10606
10607     /**
10608      * Query the records by a specified property.
10609      * @param {String} field A field on your records
10610      * @param {String/RegExp} value Either a string that the field
10611      * should start with or a RegExp to test against the field
10612      * @param {Boolean} anyMatch True to match any part not just the beginning
10613      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10614      */
10615     query : function(property, value, anyMatch){
10616         var fn = this.createFilterFn(property, value, anyMatch);
10617         return fn ? this.queryBy(fn) : this.data.clone();
10618     },
10619
10620     /**
10621      * Query by a function. The specified function will be called with each
10622      * record in this data source. If the function returns true the record is included
10623      * in the results.
10624      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10625      * @param {Object} scope (optional) The scope of the function (defaults to this)
10626       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10627      **/
10628     queryBy : function(fn, scope){
10629         var data = this.snapshot || this.data;
10630         return data.filterBy(fn, scope||this);
10631     },
10632
10633     /**
10634      * Collects unique values for a particular dataIndex from this store.
10635      * @param {String} dataIndex The property to collect
10636      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10637      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10638      * @return {Array} An array of the unique values
10639      **/
10640     collect : function(dataIndex, allowNull, bypassFilter){
10641         var d = (bypassFilter === true && this.snapshot) ?
10642                 this.snapshot.items : this.data.items;
10643         var v, sv, r = [], l = {};
10644         for(var i = 0, len = d.length; i < len; i++){
10645             v = d[i].data[dataIndex];
10646             sv = String(v);
10647             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10648                 l[sv] = true;
10649                 r[r.length] = v;
10650             }
10651         }
10652         return r;
10653     },
10654
10655     /**
10656      * Revert to a view of the Record cache with no filtering applied.
10657      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10658      */
10659     clearFilter : function(suppressEvent){
10660         if(this.snapshot && this.snapshot != this.data){
10661             this.data = this.snapshot;
10662             delete this.snapshot;
10663             if(suppressEvent !== true){
10664                 this.fireEvent("datachanged", this);
10665             }
10666         }
10667     },
10668
10669     // private
10670     afterEdit : function(record){
10671         if(this.modified.indexOf(record) == -1){
10672             this.modified.push(record);
10673         }
10674         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10675     },
10676     
10677     // private
10678     afterReject : function(record){
10679         this.modified.remove(record);
10680         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10681     },
10682
10683     // private
10684     afterCommit : function(record){
10685         this.modified.remove(record);
10686         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10687     },
10688
10689     /**
10690      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10691      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10692      */
10693     commitChanges : function(){
10694         var m = this.modified.slice(0);
10695         this.modified = [];
10696         for(var i = 0, len = m.length; i < len; i++){
10697             m[i].commit();
10698         }
10699     },
10700
10701     /**
10702      * Cancel outstanding changes on all changed records.
10703      */
10704     rejectChanges : function(){
10705         var m = this.modified.slice(0);
10706         this.modified = [];
10707         for(var i = 0, len = m.length; i < len; i++){
10708             m[i].reject();
10709         }
10710     },
10711
10712     onMetaChange : function(meta, rtype, o){
10713         this.recordType = rtype;
10714         this.fields = rtype.prototype.fields;
10715         delete this.snapshot;
10716         this.sortInfo = meta.sortInfo || this.sortInfo;
10717         this.modified = [];
10718         this.fireEvent('metachange', this, this.reader.meta);
10719     },
10720     
10721     moveIndex : function(data, type)
10722     {
10723         var index = this.indexOf(data);
10724         
10725         var newIndex = index + type;
10726         
10727         this.remove(data);
10728         
10729         this.insert(newIndex, data);
10730         
10731     }
10732 });/*
10733  * Based on:
10734  * Ext JS Library 1.1.1
10735  * Copyright(c) 2006-2007, Ext JS, LLC.
10736  *
10737  * Originally Released Under LGPL - original licence link has changed is not relivant.
10738  *
10739  * Fork - LGPL
10740  * <script type="text/javascript">
10741  */
10742
10743 /**
10744  * @class Roo.data.SimpleStore
10745  * @extends Roo.data.Store
10746  * Small helper class to make creating Stores from Array data easier.
10747  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10748  * @cfg {Array} fields An array of field definition objects, or field name strings.
10749  * @cfg {Array} data The multi-dimensional array of data
10750  * @constructor
10751  * @param {Object} config
10752  */
10753 Roo.data.SimpleStore = function(config){
10754     Roo.data.SimpleStore.superclass.constructor.call(this, {
10755         isLocal : true,
10756         reader: new Roo.data.ArrayReader({
10757                 id: config.id
10758             },
10759             Roo.data.Record.create(config.fields)
10760         ),
10761         proxy : new Roo.data.MemoryProxy(config.data)
10762     });
10763     this.load();
10764 };
10765 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10766  * Based on:
10767  * Ext JS Library 1.1.1
10768  * Copyright(c) 2006-2007, Ext JS, LLC.
10769  *
10770  * Originally Released Under LGPL - original licence link has changed is not relivant.
10771  *
10772  * Fork - LGPL
10773  * <script type="text/javascript">
10774  */
10775
10776 /**
10777 /**
10778  * @extends Roo.data.Store
10779  * @class Roo.data.JsonStore
10780  * Small helper class to make creating Stores for JSON data easier. <br/>
10781 <pre><code>
10782 var store = new Roo.data.JsonStore({
10783     url: 'get-images.php',
10784     root: 'images',
10785     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10786 });
10787 </code></pre>
10788  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10789  * JsonReader and HttpProxy (unless inline data is provided).</b>
10790  * @cfg {Array} fields An array of field definition objects, or field name strings.
10791  * @constructor
10792  * @param {Object} config
10793  */
10794 Roo.data.JsonStore = function(c){
10795     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10796         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10797         reader: new Roo.data.JsonReader(c, c.fields)
10798     }));
10799 };
10800 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10801  * Based on:
10802  * Ext JS Library 1.1.1
10803  * Copyright(c) 2006-2007, Ext JS, LLC.
10804  *
10805  * Originally Released Under LGPL - original licence link has changed is not relivant.
10806  *
10807  * Fork - LGPL
10808  * <script type="text/javascript">
10809  */
10810
10811  
10812 Roo.data.Field = function(config){
10813     if(typeof config == "string"){
10814         config = {name: config};
10815     }
10816     Roo.apply(this, config);
10817     
10818     if(!this.type){
10819         this.type = "auto";
10820     }
10821     
10822     var st = Roo.data.SortTypes;
10823     // named sortTypes are supported, here we look them up
10824     if(typeof this.sortType == "string"){
10825         this.sortType = st[this.sortType];
10826     }
10827     
10828     // set default sortType for strings and dates
10829     if(!this.sortType){
10830         switch(this.type){
10831             case "string":
10832                 this.sortType = st.asUCString;
10833                 break;
10834             case "date":
10835                 this.sortType = st.asDate;
10836                 break;
10837             default:
10838                 this.sortType = st.none;
10839         }
10840     }
10841
10842     // define once
10843     var stripRe = /[\$,%]/g;
10844
10845     // prebuilt conversion function for this field, instead of
10846     // switching every time we're reading a value
10847     if(!this.convert){
10848         var cv, dateFormat = this.dateFormat;
10849         switch(this.type){
10850             case "":
10851             case "auto":
10852             case undefined:
10853                 cv = function(v){ return v; };
10854                 break;
10855             case "string":
10856                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10857                 break;
10858             case "int":
10859                 cv = function(v){
10860                     return v !== undefined && v !== null && v !== '' ?
10861                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10862                     };
10863                 break;
10864             case "float":
10865                 cv = function(v){
10866                     return v !== undefined && v !== null && v !== '' ?
10867                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10868                     };
10869                 break;
10870             case "bool":
10871             case "boolean":
10872                 cv = function(v){ return v === true || v === "true" || v == 1; };
10873                 break;
10874             case "date":
10875                 cv = function(v){
10876                     if(!v){
10877                         return '';
10878                     }
10879                     if(v instanceof Date){
10880                         return v;
10881                     }
10882                     if(dateFormat){
10883                         if(dateFormat == "timestamp"){
10884                             return new Date(v*1000);
10885                         }
10886                         return Date.parseDate(v, dateFormat);
10887                     }
10888                     var parsed = Date.parse(v);
10889                     return parsed ? new Date(parsed) : null;
10890                 };
10891              break;
10892             
10893         }
10894         this.convert = cv;
10895     }
10896 };
10897
10898 Roo.data.Field.prototype = {
10899     dateFormat: null,
10900     defaultValue: "",
10901     mapping: null,
10902     sortType : null,
10903     sortDir : "ASC"
10904 };/*
10905  * Based on:
10906  * Ext JS Library 1.1.1
10907  * Copyright(c) 2006-2007, Ext JS, LLC.
10908  *
10909  * Originally Released Under LGPL - original licence link has changed is not relivant.
10910  *
10911  * Fork - LGPL
10912  * <script type="text/javascript">
10913  */
10914  
10915 // Base class for reading structured data from a data source.  This class is intended to be
10916 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10917
10918 /**
10919  * @class Roo.data.DataReader
10920  * Base class for reading structured data from a data source.  This class is intended to be
10921  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10922  */
10923
10924 Roo.data.DataReader = function(meta, recordType){
10925     
10926     this.meta = meta;
10927     
10928     this.recordType = recordType instanceof Array ? 
10929         Roo.data.Record.create(recordType) : recordType;
10930 };
10931
10932 Roo.data.DataReader.prototype = {
10933      /**
10934      * Create an empty record
10935      * @param {Object} data (optional) - overlay some values
10936      * @return {Roo.data.Record} record created.
10937      */
10938     newRow :  function(d) {
10939         var da =  {};
10940         this.recordType.prototype.fields.each(function(c) {
10941             switch( c.type) {
10942                 case 'int' : da[c.name] = 0; break;
10943                 case 'date' : da[c.name] = new Date(); break;
10944                 case 'float' : da[c.name] = 0.0; break;
10945                 case 'boolean' : da[c.name] = false; break;
10946                 default : da[c.name] = ""; break;
10947             }
10948             
10949         });
10950         return new this.recordType(Roo.apply(da, d));
10951     }
10952     
10953 };/*
10954  * Based on:
10955  * Ext JS Library 1.1.1
10956  * Copyright(c) 2006-2007, Ext JS, LLC.
10957  *
10958  * Originally Released Under LGPL - original licence link has changed is not relivant.
10959  *
10960  * Fork - LGPL
10961  * <script type="text/javascript">
10962  */
10963
10964 /**
10965  * @class Roo.data.DataProxy
10966  * @extends Roo.data.Observable
10967  * This class is an abstract base class for implementations which provide retrieval of
10968  * unformatted data objects.<br>
10969  * <p>
10970  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10971  * (of the appropriate type which knows how to parse the data object) to provide a block of
10972  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10973  * <p>
10974  * Custom implementations must implement the load method as described in
10975  * {@link Roo.data.HttpProxy#load}.
10976  */
10977 Roo.data.DataProxy = function(){
10978     this.addEvents({
10979         /**
10980          * @event beforeload
10981          * Fires before a network request is made to retrieve a data object.
10982          * @param {Object} This DataProxy object.
10983          * @param {Object} params The params parameter to the load function.
10984          */
10985         beforeload : true,
10986         /**
10987          * @event load
10988          * Fires before the load method's callback is called.
10989          * @param {Object} This DataProxy object.
10990          * @param {Object} o The data object.
10991          * @param {Object} arg The callback argument object passed to the load function.
10992          */
10993         load : true,
10994         /**
10995          * @event loadexception
10996          * Fires if an Exception occurs during data retrieval.
10997          * @param {Object} This DataProxy object.
10998          * @param {Object} o The data object.
10999          * @param {Object} arg The callback argument object passed to the load function.
11000          * @param {Object} e The Exception.
11001          */
11002         loadexception : true
11003     });
11004     Roo.data.DataProxy.superclass.constructor.call(this);
11005 };
11006
11007 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11008
11009     /**
11010      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11011      */
11012 /*
11013  * Based on:
11014  * Ext JS Library 1.1.1
11015  * Copyright(c) 2006-2007, Ext JS, LLC.
11016  *
11017  * Originally Released Under LGPL - original licence link has changed is not relivant.
11018  *
11019  * Fork - LGPL
11020  * <script type="text/javascript">
11021  */
11022 /**
11023  * @class Roo.data.MemoryProxy
11024  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11025  * to the Reader when its load method is called.
11026  * @constructor
11027  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11028  */
11029 Roo.data.MemoryProxy = function(data){
11030     if (data.data) {
11031         data = data.data;
11032     }
11033     Roo.data.MemoryProxy.superclass.constructor.call(this);
11034     this.data = data;
11035 };
11036
11037 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11038     
11039     /**
11040      * Load data from the requested source (in this case an in-memory
11041      * data object passed to the constructor), read the data object into
11042      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11043      * process that block using the passed callback.
11044      * @param {Object} params This parameter is not used by the MemoryProxy class.
11045      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11046      * object into a block of Roo.data.Records.
11047      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11048      * The function must be passed <ul>
11049      * <li>The Record block object</li>
11050      * <li>The "arg" argument from the load function</li>
11051      * <li>A boolean success indicator</li>
11052      * </ul>
11053      * @param {Object} scope The scope in which to call the callback
11054      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11055      */
11056     load : function(params, reader, callback, scope, arg){
11057         params = params || {};
11058         var result;
11059         try {
11060             result = reader.readRecords(this.data);
11061         }catch(e){
11062             this.fireEvent("loadexception", this, arg, null, e);
11063             callback.call(scope, null, arg, false);
11064             return;
11065         }
11066         callback.call(scope, result, arg, true);
11067     },
11068     
11069     // private
11070     update : function(params, records){
11071         
11072     }
11073 });/*
11074  * Based on:
11075  * Ext JS Library 1.1.1
11076  * Copyright(c) 2006-2007, Ext JS, LLC.
11077  *
11078  * Originally Released Under LGPL - original licence link has changed is not relivant.
11079  *
11080  * Fork - LGPL
11081  * <script type="text/javascript">
11082  */
11083 /**
11084  * @class Roo.data.HttpProxy
11085  * @extends Roo.data.DataProxy
11086  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11087  * configured to reference a certain URL.<br><br>
11088  * <p>
11089  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11090  * from which the running page was served.<br><br>
11091  * <p>
11092  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11093  * <p>
11094  * Be aware that to enable the browser to parse an XML document, the server must set
11095  * the Content-Type header in the HTTP response to "text/xml".
11096  * @constructor
11097  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11098  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11099  * will be used to make the request.
11100  */
11101 Roo.data.HttpProxy = function(conn){
11102     Roo.data.HttpProxy.superclass.constructor.call(this);
11103     // is conn a conn config or a real conn?
11104     this.conn = conn;
11105     this.useAjax = !conn || !conn.events;
11106   
11107 };
11108
11109 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11110     // thse are take from connection...
11111     
11112     /**
11113      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11114      */
11115     /**
11116      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11117      * extra parameters to each request made by this object. (defaults to undefined)
11118      */
11119     /**
11120      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11121      *  to each request made by this object. (defaults to undefined)
11122      */
11123     /**
11124      * @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)
11125      */
11126     /**
11127      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11128      */
11129      /**
11130      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11131      * @type Boolean
11132      */
11133   
11134
11135     /**
11136      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11137      * @type Boolean
11138      */
11139     /**
11140      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11141      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11142      * a finer-grained basis than the DataProxy events.
11143      */
11144     getConnection : function(){
11145         return this.useAjax ? Roo.Ajax : this.conn;
11146     },
11147
11148     /**
11149      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11150      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11151      * process that block using the passed callback.
11152      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11153      * for the request to the remote server.
11154      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11155      * object into a block of Roo.data.Records.
11156      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11157      * The function must be passed <ul>
11158      * <li>The Record block object</li>
11159      * <li>The "arg" argument from the load function</li>
11160      * <li>A boolean success indicator</li>
11161      * </ul>
11162      * @param {Object} scope The scope in which to call the callback
11163      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11164      */
11165     load : function(params, reader, callback, scope, arg){
11166         if(this.fireEvent("beforeload", this, params) !== false){
11167             var  o = {
11168                 params : params || {},
11169                 request: {
11170                     callback : callback,
11171                     scope : scope,
11172                     arg : arg
11173                 },
11174                 reader: reader,
11175                 callback : this.loadResponse,
11176                 scope: this
11177             };
11178             if(this.useAjax){
11179                 Roo.applyIf(o, this.conn);
11180                 if(this.activeRequest){
11181                     Roo.Ajax.abort(this.activeRequest);
11182                 }
11183                 this.activeRequest = Roo.Ajax.request(o);
11184             }else{
11185                 this.conn.request(o);
11186             }
11187         }else{
11188             callback.call(scope||this, null, arg, false);
11189         }
11190     },
11191
11192     // private
11193     loadResponse : function(o, success, response){
11194         delete this.activeRequest;
11195         if(!success){
11196             this.fireEvent("loadexception", this, o, response);
11197             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11198             return;
11199         }
11200         var result;
11201         try {
11202             result = o.reader.read(response);
11203         }catch(e){
11204             this.fireEvent("loadexception", this, o, response, e);
11205             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11206             return;
11207         }
11208         
11209         this.fireEvent("load", this, o, o.request.arg);
11210         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11211     },
11212
11213     // private
11214     update : function(dataSet){
11215
11216     },
11217
11218     // private
11219     updateResponse : function(dataSet){
11220
11221     }
11222 });/*
11223  * Based on:
11224  * Ext JS Library 1.1.1
11225  * Copyright(c) 2006-2007, Ext JS, LLC.
11226  *
11227  * Originally Released Under LGPL - original licence link has changed is not relivant.
11228  *
11229  * Fork - LGPL
11230  * <script type="text/javascript">
11231  */
11232
11233 /**
11234  * @class Roo.data.ScriptTagProxy
11235  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11236  * other than the originating domain of the running page.<br><br>
11237  * <p>
11238  * <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
11239  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11240  * <p>
11241  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11242  * source code that is used as the source inside a &lt;script> tag.<br><br>
11243  * <p>
11244  * In order for the browser to process the returned data, the server must wrap the data object
11245  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11246  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11247  * depending on whether the callback name was passed:
11248  * <p>
11249  * <pre><code>
11250 boolean scriptTag = false;
11251 String cb = request.getParameter("callback");
11252 if (cb != null) {
11253     scriptTag = true;
11254     response.setContentType("text/javascript");
11255 } else {
11256     response.setContentType("application/x-json");
11257 }
11258 Writer out = response.getWriter();
11259 if (scriptTag) {
11260     out.write(cb + "(");
11261 }
11262 out.print(dataBlock.toJsonString());
11263 if (scriptTag) {
11264     out.write(");");
11265 }
11266 </pre></code>
11267  *
11268  * @constructor
11269  * @param {Object} config A configuration object.
11270  */
11271 Roo.data.ScriptTagProxy = function(config){
11272     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11273     Roo.apply(this, config);
11274     this.head = document.getElementsByTagName("head")[0];
11275 };
11276
11277 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11278
11279 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11280     /**
11281      * @cfg {String} url The URL from which to request the data object.
11282      */
11283     /**
11284      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11285      */
11286     timeout : 30000,
11287     /**
11288      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11289      * the server the name of the callback function set up by the load call to process the returned data object.
11290      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11291      * javascript output which calls this named function passing the data object as its only parameter.
11292      */
11293     callbackParam : "callback",
11294     /**
11295      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11296      * name to the request.
11297      */
11298     nocache : true,
11299
11300     /**
11301      * Load data from the configured URL, read the data object into
11302      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11303      * process that block using the passed callback.
11304      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11305      * for the request to the remote server.
11306      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11307      * object into a block of Roo.data.Records.
11308      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11309      * The function must be passed <ul>
11310      * <li>The Record block object</li>
11311      * <li>The "arg" argument from the load function</li>
11312      * <li>A boolean success indicator</li>
11313      * </ul>
11314      * @param {Object} scope The scope in which to call the callback
11315      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11316      */
11317     load : function(params, reader, callback, scope, arg){
11318         if(this.fireEvent("beforeload", this, params) !== false){
11319
11320             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11321
11322             var url = this.url;
11323             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11324             if(this.nocache){
11325                 url += "&_dc=" + (new Date().getTime());
11326             }
11327             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11328             var trans = {
11329                 id : transId,
11330                 cb : "stcCallback"+transId,
11331                 scriptId : "stcScript"+transId,
11332                 params : params,
11333                 arg : arg,
11334                 url : url,
11335                 callback : callback,
11336                 scope : scope,
11337                 reader : reader
11338             };
11339             var conn = this;
11340
11341             window[trans.cb] = function(o){
11342                 conn.handleResponse(o, trans);
11343             };
11344
11345             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11346
11347             if(this.autoAbort !== false){
11348                 this.abort();
11349             }
11350
11351             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11352
11353             var script = document.createElement("script");
11354             script.setAttribute("src", url);
11355             script.setAttribute("type", "text/javascript");
11356             script.setAttribute("id", trans.scriptId);
11357             this.head.appendChild(script);
11358
11359             this.trans = trans;
11360         }else{
11361             callback.call(scope||this, null, arg, false);
11362         }
11363     },
11364
11365     // private
11366     isLoading : function(){
11367         return this.trans ? true : false;
11368     },
11369
11370     /**
11371      * Abort the current server request.
11372      */
11373     abort : function(){
11374         if(this.isLoading()){
11375             this.destroyTrans(this.trans);
11376         }
11377     },
11378
11379     // private
11380     destroyTrans : function(trans, isLoaded){
11381         this.head.removeChild(document.getElementById(trans.scriptId));
11382         clearTimeout(trans.timeoutId);
11383         if(isLoaded){
11384             window[trans.cb] = undefined;
11385             try{
11386                 delete window[trans.cb];
11387             }catch(e){}
11388         }else{
11389             // if hasn't been loaded, wait for load to remove it to prevent script error
11390             window[trans.cb] = function(){
11391                 window[trans.cb] = undefined;
11392                 try{
11393                     delete window[trans.cb];
11394                 }catch(e){}
11395             };
11396         }
11397     },
11398
11399     // private
11400     handleResponse : function(o, trans){
11401         this.trans = false;
11402         this.destroyTrans(trans, true);
11403         var result;
11404         try {
11405             result = trans.reader.readRecords(o);
11406         }catch(e){
11407             this.fireEvent("loadexception", this, o, trans.arg, e);
11408             trans.callback.call(trans.scope||window, null, trans.arg, false);
11409             return;
11410         }
11411         this.fireEvent("load", this, o, trans.arg);
11412         trans.callback.call(trans.scope||window, result, trans.arg, true);
11413     },
11414
11415     // private
11416     handleFailure : function(trans){
11417         this.trans = false;
11418         this.destroyTrans(trans, false);
11419         this.fireEvent("loadexception", this, null, trans.arg);
11420         trans.callback.call(trans.scope||window, null, trans.arg, false);
11421     }
11422 });/*
11423  * Based on:
11424  * Ext JS Library 1.1.1
11425  * Copyright(c) 2006-2007, Ext JS, LLC.
11426  *
11427  * Originally Released Under LGPL - original licence link has changed is not relivant.
11428  *
11429  * Fork - LGPL
11430  * <script type="text/javascript">
11431  */
11432
11433 /**
11434  * @class Roo.data.JsonReader
11435  * @extends Roo.data.DataReader
11436  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11437  * based on mappings in a provided Roo.data.Record constructor.
11438  * 
11439  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11440  * in the reply previously. 
11441  * 
11442  * <p>
11443  * Example code:
11444  * <pre><code>
11445 var RecordDef = Roo.data.Record.create([
11446     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11447     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11448 ]);
11449 var myReader = new Roo.data.JsonReader({
11450     totalProperty: "results",    // The property which contains the total dataset size (optional)
11451     root: "rows",                // The property which contains an Array of row objects
11452     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11453 }, RecordDef);
11454 </code></pre>
11455  * <p>
11456  * This would consume a JSON file like this:
11457  * <pre><code>
11458 { 'results': 2, 'rows': [
11459     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11460     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11461 }
11462 </code></pre>
11463  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11464  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11465  * paged from the remote server.
11466  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11467  * @cfg {String} root name of the property which contains the Array of row objects.
11468  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11469  * @cfg {Array} fields Array of field definition objects
11470  * @constructor
11471  * Create a new JsonReader
11472  * @param {Object} meta Metadata configuration options
11473  * @param {Object} recordType Either an Array of field definition objects,
11474  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11475  */
11476 Roo.data.JsonReader = function(meta, recordType){
11477     
11478     meta = meta || {};
11479     // set some defaults:
11480     Roo.applyIf(meta, {
11481         totalProperty: 'total',
11482         successProperty : 'success',
11483         root : 'data',
11484         id : 'id'
11485     });
11486     
11487     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11488 };
11489 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11490     
11491     /**
11492      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11493      * Used by Store query builder to append _requestMeta to params.
11494      * 
11495      */
11496     metaFromRemote : false,
11497     /**
11498      * This method is only used by a DataProxy which has retrieved data from a remote server.
11499      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11500      * @return {Object} data A data block which is used by an Roo.data.Store object as
11501      * a cache of Roo.data.Records.
11502      */
11503     read : function(response){
11504         var json = response.responseText;
11505        
11506         var o = /* eval:var:o */ eval("("+json+")");
11507         if(!o) {
11508             throw {message: "JsonReader.read: Json object not found"};
11509         }
11510         
11511         if(o.metaData){
11512             
11513             delete this.ef;
11514             this.metaFromRemote = true;
11515             this.meta = o.metaData;
11516             this.recordType = Roo.data.Record.create(o.metaData.fields);
11517             this.onMetaChange(this.meta, this.recordType, o);
11518         }
11519         return this.readRecords(o);
11520     },
11521
11522     // private function a store will implement
11523     onMetaChange : function(meta, recordType, o){
11524
11525     },
11526
11527     /**
11528          * @ignore
11529          */
11530     simpleAccess: function(obj, subsc) {
11531         return obj[subsc];
11532     },
11533
11534         /**
11535          * @ignore
11536          */
11537     getJsonAccessor: function(){
11538         var re = /[\[\.]/;
11539         return function(expr) {
11540             try {
11541                 return(re.test(expr))
11542                     ? new Function("obj", "return obj." + expr)
11543                     : function(obj){
11544                         return obj[expr];
11545                     };
11546             } catch(e){}
11547             return Roo.emptyFn;
11548         };
11549     }(),
11550
11551     /**
11552      * Create a data block containing Roo.data.Records from an XML document.
11553      * @param {Object} o An object which contains an Array of row objects in the property specified
11554      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11555      * which contains the total size of the dataset.
11556      * @return {Object} data A data block which is used by an Roo.data.Store object as
11557      * a cache of Roo.data.Records.
11558      */
11559     readRecords : function(o){
11560         /**
11561          * After any data loads, the raw JSON data is available for further custom processing.
11562          * @type Object
11563          */
11564         this.o = o;
11565         var s = this.meta, Record = this.recordType,
11566             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11567
11568 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11569         if (!this.ef) {
11570             if(s.totalProperty) {
11571                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11572                 }
11573                 if(s.successProperty) {
11574                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11575                 }
11576                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11577                 if (s.id) {
11578                         var g = this.getJsonAccessor(s.id);
11579                         this.getId = function(rec) {
11580                                 var r = g(rec);  
11581                                 return (r === undefined || r === "") ? null : r;
11582                         };
11583                 } else {
11584                         this.getId = function(){return null;};
11585                 }
11586             this.ef = [];
11587             for(var jj = 0; jj < fl; jj++){
11588                 f = fi[jj];
11589                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11590                 this.ef[jj] = this.getJsonAccessor(map);
11591             }
11592         }
11593
11594         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11595         if(s.totalProperty){
11596             var vt = parseInt(this.getTotal(o), 10);
11597             if(!isNaN(vt)){
11598                 totalRecords = vt;
11599             }
11600         }
11601         if(s.successProperty){
11602             var vs = this.getSuccess(o);
11603             if(vs === false || vs === 'false'){
11604                 success = false;
11605             }
11606         }
11607         var records = [];
11608         for(var i = 0; i < c; i++){
11609                 var n = root[i];
11610             var values = {};
11611             var id = this.getId(n);
11612             for(var j = 0; j < fl; j++){
11613                 f = fi[j];
11614             var v = this.ef[j](n);
11615             if (!f.convert) {
11616                 Roo.log('missing convert for ' + f.name);
11617                 Roo.log(f);
11618                 continue;
11619             }
11620             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11621             }
11622             var record = new Record(values, id);
11623             record.json = n;
11624             records[i] = record;
11625         }
11626         return {
11627             raw : o,
11628             success : success,
11629             records : records,
11630             totalRecords : totalRecords
11631         };
11632     }
11633 });/*
11634  * Based on:
11635  * Ext JS Library 1.1.1
11636  * Copyright(c) 2006-2007, Ext JS, LLC.
11637  *
11638  * Originally Released Under LGPL - original licence link has changed is not relivant.
11639  *
11640  * Fork - LGPL
11641  * <script type="text/javascript">
11642  */
11643
11644 /**
11645  * @class Roo.data.ArrayReader
11646  * @extends Roo.data.DataReader
11647  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11648  * Each element of that Array represents a row of data fields. The
11649  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11650  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11651  * <p>
11652  * Example code:.
11653  * <pre><code>
11654 var RecordDef = Roo.data.Record.create([
11655     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11656     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11657 ]);
11658 var myReader = new Roo.data.ArrayReader({
11659     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11660 }, RecordDef);
11661 </code></pre>
11662  * <p>
11663  * This would consume an Array like this:
11664  * <pre><code>
11665 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11666   </code></pre>
11667  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11668  * @constructor
11669  * Create a new JsonReader
11670  * @param {Object} meta Metadata configuration options.
11671  * @param {Object} recordType Either an Array of field definition objects
11672  * as specified to {@link Roo.data.Record#create},
11673  * or an {@link Roo.data.Record} object
11674  * created using {@link Roo.data.Record#create}.
11675  */
11676 Roo.data.ArrayReader = function(meta, recordType){
11677     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11678 };
11679
11680 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11681     /**
11682      * Create a data block containing Roo.data.Records from an XML document.
11683      * @param {Object} o An Array of row objects which represents the dataset.
11684      * @return {Object} data A data block which is used by an Roo.data.Store object as
11685      * a cache of Roo.data.Records.
11686      */
11687     readRecords : function(o){
11688         var sid = this.meta ? this.meta.id : null;
11689         var recordType = this.recordType, fields = recordType.prototype.fields;
11690         var records = [];
11691         var root = o;
11692             for(var i = 0; i < root.length; i++){
11693                     var n = root[i];
11694                 var values = {};
11695                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11696                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11697                 var f = fields.items[j];
11698                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11699                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11700                 v = f.convert(v);
11701                 values[f.name] = v;
11702             }
11703                 var record = new recordType(values, id);
11704                 record.json = n;
11705                 records[records.length] = record;
11706             }
11707             return {
11708                 records : records,
11709                 totalRecords : records.length
11710             };
11711     }
11712 });/*
11713  * - LGPL
11714  * * 
11715  */
11716
11717 /**
11718  * @class Roo.bootstrap.ComboBox
11719  * @extends Roo.bootstrap.TriggerField
11720  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11721  * @cfg {Boolean} append (true|false) default false
11722  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11723  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11724  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11725  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11726  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11727  * @cfg {Boolean} animate default true
11728  * @cfg {Boolean} emptyResultText only for touch device
11729  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11730  * @constructor
11731  * Create a new ComboBox.
11732  * @param {Object} config Configuration options
11733  */
11734 Roo.bootstrap.ComboBox = function(config){
11735     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11736     this.addEvents({
11737         /**
11738          * @event expand
11739          * Fires when the dropdown list is expanded
11740              * @param {Roo.bootstrap.ComboBox} combo This combo box
11741              */
11742         'expand' : true,
11743         /**
11744          * @event collapse
11745          * Fires when the dropdown list is collapsed
11746              * @param {Roo.bootstrap.ComboBox} combo This combo box
11747              */
11748         'collapse' : true,
11749         /**
11750          * @event beforeselect
11751          * Fires before a list item is selected. Return false to cancel the selection.
11752              * @param {Roo.bootstrap.ComboBox} combo This combo box
11753              * @param {Roo.data.Record} record The data record returned from the underlying store
11754              * @param {Number} index The index of the selected item in the dropdown list
11755              */
11756         'beforeselect' : true,
11757         /**
11758          * @event select
11759          * Fires when a list item is selected
11760              * @param {Roo.bootstrap.ComboBox} combo This combo box
11761              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11762              * @param {Number} index The index of the selected item in the dropdown list
11763              */
11764         'select' : true,
11765         /**
11766          * @event beforequery
11767          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11768          * The event object passed has these properties:
11769              * @param {Roo.bootstrap.ComboBox} combo This combo box
11770              * @param {String} query The query
11771              * @param {Boolean} forceAll true to force "all" query
11772              * @param {Boolean} cancel true to cancel the query
11773              * @param {Object} e The query event object
11774              */
11775         'beforequery': true,
11776          /**
11777          * @event add
11778          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11779              * @param {Roo.bootstrap.ComboBox} combo This combo box
11780              */
11781         'add' : true,
11782         /**
11783          * @event edit
11784          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11785              * @param {Roo.bootstrap.ComboBox} combo This combo box
11786              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11787              */
11788         'edit' : true,
11789         /**
11790          * @event remove
11791          * Fires when the remove value from the combobox array
11792              * @param {Roo.bootstrap.ComboBox} combo This combo box
11793              */
11794         'remove' : true,
11795         /**
11796          * @event afterremove
11797          * Fires when the remove value from the combobox array
11798              * @param {Roo.bootstrap.ComboBox} combo This combo box
11799              */
11800         'afterremove' : true,
11801         /**
11802          * @event specialfilter
11803          * Fires when specialfilter
11804             * @param {Roo.bootstrap.ComboBox} combo This combo box
11805             */
11806         'specialfilter' : true,
11807         /**
11808          * @event tick
11809          * Fires when tick the element
11810             * @param {Roo.bootstrap.ComboBox} combo This combo box
11811             */
11812         'tick' : true,
11813         /**
11814          * @event touchviewdisplay
11815          * Fires when touch view require special display (default is using displayField)
11816             * @param {Roo.bootstrap.ComboBox} combo This combo box
11817             * @param {Object} cfg set html .
11818             */
11819         'touchviewdisplay' : true
11820         
11821     });
11822     
11823     this.item = [];
11824     this.tickItems = [];
11825     
11826     this.selectedIndex = -1;
11827     if(this.mode == 'local'){
11828         if(config.queryDelay === undefined){
11829             this.queryDelay = 10;
11830         }
11831         if(config.minChars === undefined){
11832             this.minChars = 0;
11833         }
11834     }
11835 };
11836
11837 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11838      
11839     /**
11840      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11841      * rendering into an Roo.Editor, defaults to false)
11842      */
11843     /**
11844      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11845      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11846      */
11847     /**
11848      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11849      */
11850     /**
11851      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11852      * the dropdown list (defaults to undefined, with no header element)
11853      */
11854
11855      /**
11856      * @cfg {String/Roo.Template} tpl The template to use to render the output
11857      */
11858      
11859      /**
11860      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11861      */
11862     listWidth: undefined,
11863     /**
11864      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11865      * mode = 'remote' or 'text' if mode = 'local')
11866      */
11867     displayField: undefined,
11868     
11869     /**
11870      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11871      * mode = 'remote' or 'value' if mode = 'local'). 
11872      * Note: use of a valueField requires the user make a selection
11873      * in order for a value to be mapped.
11874      */
11875     valueField: undefined,
11876     
11877     
11878     /**
11879      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11880      * field's data value (defaults to the underlying DOM element's name)
11881      */
11882     hiddenName: undefined,
11883     /**
11884      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11885      */
11886     listClass: '',
11887     /**
11888      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11889      */
11890     selectedClass: 'active',
11891     
11892     /**
11893      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11894      */
11895     shadow:'sides',
11896     /**
11897      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11898      * anchor positions (defaults to 'tl-bl')
11899      */
11900     listAlign: 'tl-bl?',
11901     /**
11902      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11903      */
11904     maxHeight: 300,
11905     /**
11906      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11907      * query specified by the allQuery config option (defaults to 'query')
11908      */
11909     triggerAction: 'query',
11910     /**
11911      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11912      * (defaults to 4, does not apply if editable = false)
11913      */
11914     minChars : 4,
11915     /**
11916      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11917      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11918      */
11919     typeAhead: false,
11920     /**
11921      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11922      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11923      */
11924     queryDelay: 500,
11925     /**
11926      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11927      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11928      */
11929     pageSize: 0,
11930     /**
11931      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11932      * when editable = true (defaults to false)
11933      */
11934     selectOnFocus:false,
11935     /**
11936      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11937      */
11938     queryParam: 'query',
11939     /**
11940      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11941      * when mode = 'remote' (defaults to 'Loading...')
11942      */
11943     loadingText: 'Loading...',
11944     /**
11945      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11946      */
11947     resizable: false,
11948     /**
11949      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11950      */
11951     handleHeight : 8,
11952     /**
11953      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11954      * traditional select (defaults to true)
11955      */
11956     editable: true,
11957     /**
11958      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11959      */
11960     allQuery: '',
11961     /**
11962      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11963      */
11964     mode: 'remote',
11965     /**
11966      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11967      * listWidth has a higher value)
11968      */
11969     minListWidth : 70,
11970     /**
11971      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11972      * allow the user to set arbitrary text into the field (defaults to false)
11973      */
11974     forceSelection:false,
11975     /**
11976      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11977      * if typeAhead = true (defaults to 250)
11978      */
11979     typeAheadDelay : 250,
11980     /**
11981      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11982      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11983      */
11984     valueNotFoundText : undefined,
11985     /**
11986      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11987      */
11988     blockFocus : false,
11989     
11990     /**
11991      * @cfg {Boolean} disableClear Disable showing of clear button.
11992      */
11993     disableClear : false,
11994     /**
11995      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11996      */
11997     alwaysQuery : false,
11998     
11999     /**
12000      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12001      */
12002     multiple : false,
12003     
12004     /**
12005      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12006      */
12007     invalidClass : "has-warning",
12008     
12009     /**
12010      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12011      */
12012     validClass : "has-success",
12013     
12014     /**
12015      * @cfg {Boolean} specialFilter (true|false) special filter default false
12016      */
12017     specialFilter : false,
12018     
12019     /**
12020      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12021      */
12022     mobileTouchView : true,
12023     
12024     //private
12025     addicon : false,
12026     editicon: false,
12027     
12028     page: 0,
12029     hasQuery: false,
12030     append: false,
12031     loadNext: false,
12032     autoFocus : true,
12033     tickable : false,
12034     btnPosition : 'right',
12035     triggerList : true,
12036     showToggleBtn : true,
12037     animate : true,
12038     emptyResultText: 'Empty',
12039     triggerText : 'Select',
12040     
12041     // element that contains real text value.. (when hidden is used..)
12042     
12043     getAutoCreate : function()
12044     {
12045         var cfg = false;
12046         
12047         /*
12048          * Touch Devices
12049          */
12050         
12051         if(Roo.isTouch && this.mobileTouchView){
12052             cfg = this.getAutoCreateTouchView();
12053             return cfg;;
12054         }
12055         
12056         /*
12057          *  Normal ComboBox
12058          */
12059         if(!this.tickable){
12060             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12061             return cfg;
12062         }
12063         
12064         /*
12065          *  ComboBox with tickable selections
12066          */
12067              
12068         var align = this.labelAlign || this.parentLabelAlign();
12069         
12070         cfg = {
12071             cls : 'form-group roo-combobox-tickable' //input-group
12072         };
12073         
12074         var buttons = {
12075             tag : 'div',
12076             cls : 'tickable-buttons',
12077             cn : [
12078                 {
12079                     tag : 'button',
12080                     type : 'button',
12081                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12082                     html : this.triggerText
12083                 },
12084                 {
12085                     tag : 'button',
12086                     type : 'button',
12087                     name : 'ok',
12088                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12089                     html : 'Done'
12090                 },
12091                 {
12092                     tag : 'button',
12093                     type : 'button',
12094                     name : 'cancel',
12095                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12096                     html : 'Cancel'
12097                 }
12098             ]
12099         };
12100         
12101         if(this.editable){
12102             buttons.cn.unshift({
12103                 tag: 'input',
12104                 cls: 'roo-select2-search-field-input'
12105             });
12106         }
12107         
12108         var _this = this;
12109         
12110         Roo.each(buttons.cn, function(c){
12111             if (_this.size) {
12112                 c.cls += ' btn-' + _this.size;
12113             }
12114
12115             if (_this.disabled) {
12116                 c.disabled = true;
12117             }
12118         });
12119         
12120         var box = {
12121             tag: 'div',
12122             cn: [
12123                 {
12124                     tag: 'input',
12125                     type : 'hidden',
12126                     cls: 'form-hidden-field'
12127                 },
12128                 {
12129                     tag: 'ul',
12130                     cls: 'roo-select2-choices',
12131                     cn:[
12132                         {
12133                             tag: 'li',
12134                             cls: 'roo-select2-search-field',
12135                             cn: [
12136
12137                                 buttons
12138                             ]
12139                         }
12140                     ]
12141                 }
12142             ]
12143         };
12144         
12145         var combobox = {
12146             cls: 'roo-select2-container input-group roo-select2-container-multi',
12147             cn: [
12148                 box
12149 //                {
12150 //                    tag: 'ul',
12151 //                    cls: 'typeahead typeahead-long dropdown-menu',
12152 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12153 //                }
12154             ]
12155         };
12156         
12157         if(this.hasFeedback && !this.allowBlank){
12158             
12159             var feedback = {
12160                 tag: 'span',
12161                 cls: 'glyphicon form-control-feedback'
12162             };
12163
12164             combobox.cn.push(feedback);
12165         }
12166         
12167         if (align ==='left' && this.fieldLabel.length) {
12168             
12169 //                Roo.log("left and has label");
12170                 cfg.cn = [
12171                     
12172                     {
12173                         tag: 'label',
12174                         'for' :  id,
12175                         cls : 'control-label col-sm-' + this.labelWidth,
12176                         html : this.fieldLabel
12177                         
12178                     },
12179                     {
12180                         cls : "col-sm-" + (12 - this.labelWidth), 
12181                         cn: [
12182                             combobox
12183                         ]
12184                     }
12185                     
12186                 ];
12187         } else if ( this.fieldLabel.length) {
12188 //                Roo.log(" label");
12189                  cfg.cn = [
12190                    
12191                     {
12192                         tag: 'label',
12193                         //cls : 'input-group-addon',
12194                         html : this.fieldLabel
12195                         
12196                     },
12197                     
12198                     combobox
12199                     
12200                 ];
12201
12202         } else {
12203             
12204 //                Roo.log(" no label && no align");
12205                 cfg = combobox
12206                      
12207                 
12208         }
12209          
12210         var settings=this;
12211         ['xs','sm','md','lg'].map(function(size){
12212             if (settings[size]) {
12213                 cfg.cls += ' col-' + size + '-' + settings[size];
12214             }
12215         });
12216         
12217         return cfg;
12218         
12219     },
12220     
12221     _initEventsCalled : false,
12222     
12223     // private
12224     initEvents: function()
12225     {
12226         
12227         if (this._initEventsCalled) { // as we call render... prevent looping...
12228             return;
12229         }
12230         this._initEventsCalled = true;
12231         
12232         if (!this.store) {
12233             throw "can not find store for combo";
12234         }
12235         
12236         this.store = Roo.factory(this.store, Roo.data);
12237         
12238         // if we are building from html. then this element is so complex, that we can not really
12239         // use the rendered HTML.
12240         // so we have to trash and replace the previous code.
12241         if (Roo.XComponent.build_from_html) {
12242             
12243             // remove this element....
12244             var e = this.el.dom, k=0;
12245             while (e ) { e = e.previousSibling;  ++k;}
12246
12247             this.el.remove();
12248             
12249             this.el=false;
12250             this.rendered = false;
12251             
12252             this.render(this.parent().getChildContainer(true), k);
12253             
12254             
12255             
12256         }
12257         
12258         
12259         /*
12260          * Touch Devices
12261          */
12262         
12263         if(Roo.isTouch && this.mobileTouchView){
12264             this.initTouchView();
12265             return;
12266         }
12267         
12268         if(this.tickable){
12269             this.initTickableEvents();
12270             return;
12271         }
12272         
12273         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12274         
12275         if(this.hiddenName){
12276             
12277             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12278             
12279             this.hiddenField.dom.value =
12280                 this.hiddenValue !== undefined ? this.hiddenValue :
12281                 this.value !== undefined ? this.value : '';
12282
12283             // prevent input submission
12284             this.el.dom.removeAttribute('name');
12285             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12286              
12287              
12288         }
12289         //if(Roo.isGecko){
12290         //    this.el.dom.setAttribute('autocomplete', 'off');
12291         //}
12292         
12293         var cls = 'x-combo-list';
12294         
12295         //this.list = new Roo.Layer({
12296         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12297         //});
12298         
12299         var _this = this;
12300         
12301         (function(){
12302             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12303             _this.list.setWidth(lw);
12304         }).defer(100);
12305         
12306         this.list.on('mouseover', this.onViewOver, this);
12307         this.list.on('mousemove', this.onViewMove, this);
12308         
12309         this.list.on('scroll', this.onViewScroll, this);
12310         
12311         /*
12312         this.list.swallowEvent('mousewheel');
12313         this.assetHeight = 0;
12314
12315         if(this.title){
12316             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12317             this.assetHeight += this.header.getHeight();
12318         }
12319
12320         this.innerList = this.list.createChild({cls:cls+'-inner'});
12321         this.innerList.on('mouseover', this.onViewOver, this);
12322         this.innerList.on('mousemove', this.onViewMove, this);
12323         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12324         
12325         if(this.allowBlank && !this.pageSize && !this.disableClear){
12326             this.footer = this.list.createChild({cls:cls+'-ft'});
12327             this.pageTb = new Roo.Toolbar(this.footer);
12328            
12329         }
12330         if(this.pageSize){
12331             this.footer = this.list.createChild({cls:cls+'-ft'});
12332             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12333                     {pageSize: this.pageSize});
12334             
12335         }
12336         
12337         if (this.pageTb && this.allowBlank && !this.disableClear) {
12338             var _this = this;
12339             this.pageTb.add(new Roo.Toolbar.Fill(), {
12340                 cls: 'x-btn-icon x-btn-clear',
12341                 text: '&#160;',
12342                 handler: function()
12343                 {
12344                     _this.collapse();
12345                     _this.clearValue();
12346                     _this.onSelect(false, -1);
12347                 }
12348             });
12349         }
12350         if (this.footer) {
12351             this.assetHeight += this.footer.getHeight();
12352         }
12353         */
12354             
12355         if(!this.tpl){
12356             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12357         }
12358
12359         this.view = new Roo.View(this.list, this.tpl, {
12360             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12361         });
12362         //this.view.wrapEl.setDisplayed(false);
12363         this.view.on('click', this.onViewClick, this);
12364         
12365         
12366         
12367         this.store.on('beforeload', this.onBeforeLoad, this);
12368         this.store.on('load', this.onLoad, this);
12369         this.store.on('loadexception', this.onLoadException, this);
12370         /*
12371         if(this.resizable){
12372             this.resizer = new Roo.Resizable(this.list,  {
12373                pinned:true, handles:'se'
12374             });
12375             this.resizer.on('resize', function(r, w, h){
12376                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12377                 this.listWidth = w;
12378                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12379                 this.restrictHeight();
12380             }, this);
12381             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12382         }
12383         */
12384         if(!this.editable){
12385             this.editable = true;
12386             this.setEditable(false);
12387         }
12388         
12389         /*
12390         
12391         if (typeof(this.events.add.listeners) != 'undefined') {
12392             
12393             this.addicon = this.wrap.createChild(
12394                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12395        
12396             this.addicon.on('click', function(e) {
12397                 this.fireEvent('add', this);
12398             }, this);
12399         }
12400         if (typeof(this.events.edit.listeners) != 'undefined') {
12401             
12402             this.editicon = this.wrap.createChild(
12403                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12404             if (this.addicon) {
12405                 this.editicon.setStyle('margin-left', '40px');
12406             }
12407             this.editicon.on('click', function(e) {
12408                 
12409                 // we fire even  if inothing is selected..
12410                 this.fireEvent('edit', this, this.lastData );
12411                 
12412             }, this);
12413         }
12414         */
12415         
12416         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12417             "up" : function(e){
12418                 this.inKeyMode = true;
12419                 this.selectPrev();
12420             },
12421
12422             "down" : function(e){
12423                 if(!this.isExpanded()){
12424                     this.onTriggerClick();
12425                 }else{
12426                     this.inKeyMode = true;
12427                     this.selectNext();
12428                 }
12429             },
12430
12431             "enter" : function(e){
12432 //                this.onViewClick();
12433                 //return true;
12434                 this.collapse();
12435                 
12436                 if(this.fireEvent("specialkey", this, e)){
12437                     this.onViewClick(false);
12438                 }
12439                 
12440                 return true;
12441             },
12442
12443             "esc" : function(e){
12444                 this.collapse();
12445             },
12446
12447             "tab" : function(e){
12448                 this.collapse();
12449                 
12450                 if(this.fireEvent("specialkey", this, e)){
12451                     this.onViewClick(false);
12452                 }
12453                 
12454                 return true;
12455             },
12456
12457             scope : this,
12458
12459             doRelay : function(foo, bar, hname){
12460                 if(hname == 'down' || this.scope.isExpanded()){
12461                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12462                 }
12463                 return true;
12464             },
12465
12466             forceKeyDown: true
12467         });
12468         
12469         
12470         this.queryDelay = Math.max(this.queryDelay || 10,
12471                 this.mode == 'local' ? 10 : 250);
12472         
12473         
12474         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12475         
12476         if(this.typeAhead){
12477             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12478         }
12479         if(this.editable !== false){
12480             this.inputEl().on("keyup", this.onKeyUp, this);
12481         }
12482         if(this.forceSelection){
12483             this.inputEl().on('blur', this.doForce, this);
12484         }
12485         
12486         if(this.multiple){
12487             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12488             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12489         }
12490     },
12491     
12492     initTickableEvents: function()
12493     {   
12494         this.createList();
12495         
12496         if(this.hiddenName){
12497             
12498             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12499             
12500             this.hiddenField.dom.value =
12501                 this.hiddenValue !== undefined ? this.hiddenValue :
12502                 this.value !== undefined ? this.value : '';
12503
12504             // prevent input submission
12505             this.el.dom.removeAttribute('name');
12506             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12507              
12508              
12509         }
12510         
12511 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12512         
12513         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12514         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12515         if(this.triggerList){
12516             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12517         }
12518          
12519         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12520         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12521         
12522         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12523         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12524         
12525         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12526         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12527         
12528         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12529         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12530         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12531         
12532         this.okBtn.hide();
12533         this.cancelBtn.hide();
12534         
12535         var _this = this;
12536         
12537         (function(){
12538             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12539             _this.list.setWidth(lw);
12540         }).defer(100);
12541         
12542         this.list.on('mouseover', this.onViewOver, this);
12543         this.list.on('mousemove', this.onViewMove, this);
12544         
12545         this.list.on('scroll', this.onViewScroll, this);
12546         
12547         if(!this.tpl){
12548             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>';
12549         }
12550
12551         this.view = new Roo.View(this.list, this.tpl, {
12552             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12553         });
12554         
12555         //this.view.wrapEl.setDisplayed(false);
12556         this.view.on('click', this.onViewClick, this);
12557         
12558         
12559         
12560         this.store.on('beforeload', this.onBeforeLoad, this);
12561         this.store.on('load', this.onLoad, this);
12562         this.store.on('loadexception', this.onLoadException, this);
12563         
12564         if(this.editable){
12565             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12566                 "up" : function(e){
12567                     this.inKeyMode = true;
12568                     this.selectPrev();
12569                 },
12570
12571                 "down" : function(e){
12572                     this.inKeyMode = true;
12573                     this.selectNext();
12574                 },
12575
12576                 "enter" : function(e){
12577                     if(this.fireEvent("specialkey", this, e)){
12578                         this.onViewClick(false);
12579                     }
12580                     
12581                     return true;
12582                 },
12583
12584                 "esc" : function(e){
12585                     this.onTickableFooterButtonClick(e, false, false);
12586                 },
12587
12588                 "tab" : function(e){
12589                     this.fireEvent("specialkey", this, e);
12590                     
12591                     this.onTickableFooterButtonClick(e, false, false);
12592                     
12593                     return true;
12594                 },
12595
12596                 scope : this,
12597
12598                 doRelay : function(e, fn, key){
12599                     if(this.scope.isExpanded()){
12600                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12601                     }
12602                     return true;
12603                 },
12604
12605                 forceKeyDown: true
12606             });
12607         }
12608         
12609         this.queryDelay = Math.max(this.queryDelay || 10,
12610                 this.mode == 'local' ? 10 : 250);
12611         
12612         
12613         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12614         
12615         if(this.typeAhead){
12616             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12617         }
12618         
12619         if(this.editable !== false){
12620             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12621         }
12622         
12623     },
12624
12625     onDestroy : function(){
12626         if(this.view){
12627             this.view.setStore(null);
12628             this.view.el.removeAllListeners();
12629             this.view.el.remove();
12630             this.view.purgeListeners();
12631         }
12632         if(this.list){
12633             this.list.dom.innerHTML  = '';
12634         }
12635         
12636         if(this.store){
12637             this.store.un('beforeload', this.onBeforeLoad, this);
12638             this.store.un('load', this.onLoad, this);
12639             this.store.un('loadexception', this.onLoadException, this);
12640         }
12641         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12642     },
12643
12644     // private
12645     fireKey : function(e){
12646         if(e.isNavKeyPress() && !this.list.isVisible()){
12647             this.fireEvent("specialkey", this, e);
12648         }
12649     },
12650
12651     // private
12652     onResize: function(w, h){
12653 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12654 //        
12655 //        if(typeof w != 'number'){
12656 //            // we do not handle it!?!?
12657 //            return;
12658 //        }
12659 //        var tw = this.trigger.getWidth();
12660 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12661 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12662 //        var x = w - tw;
12663 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12664 //            
12665 //        //this.trigger.setStyle('left', x+'px');
12666 //        
12667 //        if(this.list && this.listWidth === undefined){
12668 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12669 //            this.list.setWidth(lw);
12670 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12671 //        }
12672         
12673     
12674         
12675     },
12676
12677     /**
12678      * Allow or prevent the user from directly editing the field text.  If false is passed,
12679      * the user will only be able to select from the items defined in the dropdown list.  This method
12680      * is the runtime equivalent of setting the 'editable' config option at config time.
12681      * @param {Boolean} value True to allow the user to directly edit the field text
12682      */
12683     setEditable : function(value){
12684         if(value == this.editable){
12685             return;
12686         }
12687         this.editable = value;
12688         if(!value){
12689             this.inputEl().dom.setAttribute('readOnly', true);
12690             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12691             this.inputEl().addClass('x-combo-noedit');
12692         }else{
12693             this.inputEl().dom.setAttribute('readOnly', false);
12694             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12695             this.inputEl().removeClass('x-combo-noedit');
12696         }
12697     },
12698
12699     // private
12700     
12701     onBeforeLoad : function(combo,opts){
12702         if(!this.hasFocus){
12703             return;
12704         }
12705          if (!opts.add) {
12706             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12707          }
12708         this.restrictHeight();
12709         this.selectedIndex = -1;
12710     },
12711
12712     // private
12713     onLoad : function(){
12714         
12715         this.hasQuery = false;
12716         
12717         if(!this.hasFocus){
12718             return;
12719         }
12720         
12721         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12722             this.loading.hide();
12723         }
12724              
12725         if(this.store.getCount() > 0){
12726             this.expand();
12727             this.restrictHeight();
12728             if(this.lastQuery == this.allQuery){
12729                 if(this.editable && !this.tickable){
12730                     this.inputEl().dom.select();
12731                 }
12732                 
12733                 if(
12734                     !this.selectByValue(this.value, true) &&
12735                     this.autoFocus && 
12736                     (
12737                         !this.store.lastOptions ||
12738                         typeof(this.store.lastOptions.add) == 'undefined' || 
12739                         this.store.lastOptions.add != true
12740                     )
12741                 ){
12742                     this.select(0, true);
12743                 }
12744             }else{
12745                 if(this.autoFocus){
12746                     this.selectNext();
12747                 }
12748                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12749                     this.taTask.delay(this.typeAheadDelay);
12750                 }
12751             }
12752         }else{
12753             this.onEmptyResults();
12754         }
12755         
12756         //this.el.focus();
12757     },
12758     // private
12759     onLoadException : function()
12760     {
12761         this.hasQuery = false;
12762         
12763         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12764             this.loading.hide();
12765         }
12766         
12767         if(this.tickable && this.editable){
12768             return;
12769         }
12770         
12771         this.collapse();
12772         // only causes errors at present
12773         //Roo.log(this.store.reader.jsonData);
12774         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12775             // fixme
12776             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12777         //}
12778         
12779         
12780     },
12781     // private
12782     onTypeAhead : function(){
12783         if(this.store.getCount() > 0){
12784             var r = this.store.getAt(0);
12785             var newValue = r.data[this.displayField];
12786             var len = newValue.length;
12787             var selStart = this.getRawValue().length;
12788             
12789             if(selStart != len){
12790                 this.setRawValue(newValue);
12791                 this.selectText(selStart, newValue.length);
12792             }
12793         }
12794     },
12795
12796     // private
12797     onSelect : function(record, index){
12798         
12799         if(this.fireEvent('beforeselect', this, record, index) !== false){
12800         
12801             this.setFromData(index > -1 ? record.data : false);
12802             
12803             this.collapse();
12804             this.fireEvent('select', this, record, index);
12805         }
12806     },
12807
12808     /**
12809      * Returns the currently selected field value or empty string if no value is set.
12810      * @return {String} value The selected value
12811      */
12812     getValue : function(){
12813         
12814         if(this.multiple){
12815             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12816         }
12817         
12818         if(this.valueField){
12819             return typeof this.value != 'undefined' ? this.value : '';
12820         }else{
12821             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12822         }
12823     },
12824
12825     /**
12826      * Clears any text/value currently set in the field
12827      */
12828     clearValue : function(){
12829         if(this.hiddenField){
12830             this.hiddenField.dom.value = '';
12831         }
12832         this.value = '';
12833         this.setRawValue('');
12834         this.lastSelectionText = '';
12835         this.lastData = false;
12836         
12837         var close = this.closeTriggerEl();
12838         
12839         if(close){
12840             close.hide();
12841         }
12842         
12843     },
12844
12845     /**
12846      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12847      * will be displayed in the field.  If the value does not match the data value of an existing item,
12848      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12849      * Otherwise the field will be blank (although the value will still be set).
12850      * @param {String} value The value to match
12851      */
12852     setValue : function(v){
12853         if(this.multiple){
12854             this.syncValue();
12855             return;
12856         }
12857         
12858         var text = v;
12859         if(this.valueField){
12860             var r = this.findRecord(this.valueField, v);
12861             if(r){
12862                 text = r.data[this.displayField];
12863             }else if(this.valueNotFoundText !== undefined){
12864                 text = this.valueNotFoundText;
12865             }
12866         }
12867         this.lastSelectionText = text;
12868         if(this.hiddenField){
12869             this.hiddenField.dom.value = v;
12870         }
12871         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12872         this.value = v;
12873         
12874         var close = this.closeTriggerEl();
12875         
12876         if(close){
12877             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12878         }
12879     },
12880     /**
12881      * @property {Object} the last set data for the element
12882      */
12883     
12884     lastData : false,
12885     /**
12886      * Sets the value of the field based on a object which is related to the record format for the store.
12887      * @param {Object} value the value to set as. or false on reset?
12888      */
12889     setFromData : function(o){
12890         
12891         if(this.multiple){
12892             this.addItem(o);
12893             return;
12894         }
12895             
12896         var dv = ''; // display value
12897         var vv = ''; // value value..
12898         this.lastData = o;
12899         if (this.displayField) {
12900             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12901         } else {
12902             // this is an error condition!!!
12903             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12904         }
12905         
12906         if(this.valueField){
12907             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12908         }
12909         
12910         var close = this.closeTriggerEl();
12911         
12912         if(close){
12913             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12914         }
12915         
12916         if(this.hiddenField){
12917             this.hiddenField.dom.value = vv;
12918             
12919             this.lastSelectionText = dv;
12920             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12921             this.value = vv;
12922             return;
12923         }
12924         // no hidden field.. - we store the value in 'value', but still display
12925         // display field!!!!
12926         this.lastSelectionText = dv;
12927         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12928         this.value = vv;
12929         
12930         
12931         
12932     },
12933     // private
12934     reset : function(){
12935         // overridden so that last data is reset..
12936         
12937         if(this.multiple){
12938             this.clearItem();
12939             return;
12940         }
12941         
12942         this.setValue(this.originalValue);
12943         this.clearInvalid();
12944         this.lastData = false;
12945         if (this.view) {
12946             this.view.clearSelections();
12947         }
12948     },
12949     // private
12950     findRecord : function(prop, value){
12951         var record;
12952         if(this.store.getCount() > 0){
12953             this.store.each(function(r){
12954                 if(r.data[prop] == value){
12955                     record = r;
12956                     return false;
12957                 }
12958                 return true;
12959             });
12960         }
12961         return record;
12962     },
12963     
12964     getName: function()
12965     {
12966         // returns hidden if it's set..
12967         if (!this.rendered) {return ''};
12968         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12969         
12970     },
12971     // private
12972     onViewMove : function(e, t){
12973         this.inKeyMode = false;
12974     },
12975
12976     // private
12977     onViewOver : function(e, t){
12978         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12979             return;
12980         }
12981         var item = this.view.findItemFromChild(t);
12982         
12983         if(item){
12984             var index = this.view.indexOf(item);
12985             this.select(index, false);
12986         }
12987     },
12988
12989     // private
12990     onViewClick : function(view, doFocus, el, e)
12991     {
12992         var index = this.view.getSelectedIndexes()[0];
12993         
12994         var r = this.store.getAt(index);
12995         
12996         if(this.tickable){
12997             
12998             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12999                 return;
13000             }
13001             
13002             var rm = false;
13003             var _this = this;
13004             
13005             Roo.each(this.tickItems, function(v,k){
13006                 
13007                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13008                     Roo.log(v);
13009                     _this.tickItems.splice(k, 1);
13010                     
13011                     if(typeof(e) == 'undefined' && view == false){
13012                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13013                     }
13014                     
13015                     rm = true;
13016                     return;
13017                 }
13018             });
13019             
13020             if(rm){
13021                 return;
13022             }
13023             
13024             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13025                 this.tickItems.push(r.data);
13026             }
13027             
13028             if(typeof(e) == 'undefined' && view == false){
13029                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13030             }
13031                     
13032             return;
13033         }
13034         
13035         if(r){
13036             this.onSelect(r, index);
13037         }
13038         if(doFocus !== false && !this.blockFocus){
13039             this.inputEl().focus();
13040         }
13041     },
13042
13043     // private
13044     restrictHeight : function(){
13045         //this.innerList.dom.style.height = '';
13046         //var inner = this.innerList.dom;
13047         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13048         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13049         //this.list.beginUpdate();
13050         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13051         this.list.alignTo(this.inputEl(), this.listAlign);
13052         this.list.alignTo(this.inputEl(), this.listAlign);
13053         //this.list.endUpdate();
13054     },
13055
13056     // private
13057     onEmptyResults : function(){
13058         
13059         if(this.tickable && this.editable){
13060             this.restrictHeight();
13061             return;
13062         }
13063         
13064         this.collapse();
13065     },
13066
13067     /**
13068      * Returns true if the dropdown list is expanded, else false.
13069      */
13070     isExpanded : function(){
13071         return this.list.isVisible();
13072     },
13073
13074     /**
13075      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13076      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13077      * @param {String} value The data value of the item to select
13078      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13079      * selected item if it is not currently in view (defaults to true)
13080      * @return {Boolean} True if the value matched an item in the list, else false
13081      */
13082     selectByValue : function(v, scrollIntoView){
13083         if(v !== undefined && v !== null){
13084             var r = this.findRecord(this.valueField || this.displayField, v);
13085             if(r){
13086                 this.select(this.store.indexOf(r), scrollIntoView);
13087                 return true;
13088             }
13089         }
13090         return false;
13091     },
13092
13093     /**
13094      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13095      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13096      * @param {Number} index The zero-based index of the list item to select
13097      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13098      * selected item if it is not currently in view (defaults to true)
13099      */
13100     select : function(index, scrollIntoView){
13101         this.selectedIndex = index;
13102         this.view.select(index);
13103         if(scrollIntoView !== false){
13104             var el = this.view.getNode(index);
13105             /*
13106              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13107              */
13108             if(el){
13109                 this.list.scrollChildIntoView(el, false);
13110             }
13111         }
13112     },
13113
13114     // private
13115     selectNext : function(){
13116         var ct = this.store.getCount();
13117         if(ct > 0){
13118             if(this.selectedIndex == -1){
13119                 this.select(0);
13120             }else if(this.selectedIndex < ct-1){
13121                 this.select(this.selectedIndex+1);
13122             }
13123         }
13124     },
13125
13126     // private
13127     selectPrev : function(){
13128         var ct = this.store.getCount();
13129         if(ct > 0){
13130             if(this.selectedIndex == -1){
13131                 this.select(0);
13132             }else if(this.selectedIndex != 0){
13133                 this.select(this.selectedIndex-1);
13134             }
13135         }
13136     },
13137
13138     // private
13139     onKeyUp : function(e){
13140         if(this.editable !== false && !e.isSpecialKey()){
13141             this.lastKey = e.getKey();
13142             this.dqTask.delay(this.queryDelay);
13143         }
13144     },
13145
13146     // private
13147     validateBlur : function(){
13148         return !this.list || !this.list.isVisible();   
13149     },
13150
13151     // private
13152     initQuery : function(){
13153         
13154         var v = this.getRawValue();
13155         
13156         if(this.tickable && this.editable){
13157             v = this.tickableInputEl().getValue();
13158         }
13159         
13160         this.doQuery(v);
13161     },
13162
13163     // private
13164     doForce : function(){
13165         if(this.inputEl().dom.value.length > 0){
13166             this.inputEl().dom.value =
13167                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13168              
13169         }
13170     },
13171
13172     /**
13173      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13174      * query allowing the query action to be canceled if needed.
13175      * @param {String} query The SQL query to execute
13176      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13177      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13178      * saved in the current store (defaults to false)
13179      */
13180     doQuery : function(q, forceAll){
13181         
13182         if(q === undefined || q === null){
13183             q = '';
13184         }
13185         var qe = {
13186             query: q,
13187             forceAll: forceAll,
13188             combo: this,
13189             cancel:false
13190         };
13191         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13192             return false;
13193         }
13194         q = qe.query;
13195         
13196         forceAll = qe.forceAll;
13197         if(forceAll === true || (q.length >= this.minChars)){
13198             
13199             this.hasQuery = true;
13200             
13201             if(this.lastQuery != q || this.alwaysQuery){
13202                 this.lastQuery = q;
13203                 if(this.mode == 'local'){
13204                     this.selectedIndex = -1;
13205                     if(forceAll){
13206                         this.store.clearFilter();
13207                     }else{
13208                         
13209                         if(this.specialFilter){
13210                             this.fireEvent('specialfilter', this);
13211                             this.onLoad();
13212                             return;
13213                         }
13214                         
13215                         this.store.filter(this.displayField, q);
13216                     }
13217                     
13218                     this.store.fireEvent("datachanged", this.store);
13219                     
13220                     this.onLoad();
13221                     
13222                     
13223                 }else{
13224                     
13225                     this.store.baseParams[this.queryParam] = q;
13226                     
13227                     var options = {params : this.getParams(q)};
13228                     
13229                     if(this.loadNext){
13230                         options.add = true;
13231                         options.params.start = this.page * this.pageSize;
13232                     }
13233                     
13234                     this.store.load(options);
13235                     
13236                     /*
13237                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13238                      *  we should expand the list on onLoad
13239                      *  so command out it
13240                      */
13241 //                    this.expand();
13242                 }
13243             }else{
13244                 this.selectedIndex = -1;
13245                 this.onLoad();   
13246             }
13247         }
13248         
13249         this.loadNext = false;
13250     },
13251     
13252     // private
13253     getParams : function(q){
13254         var p = {};
13255         //p[this.queryParam] = q;
13256         
13257         if(this.pageSize){
13258             p.start = 0;
13259             p.limit = this.pageSize;
13260         }
13261         return p;
13262     },
13263
13264     /**
13265      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13266      */
13267     collapse : function(){
13268         if(!this.isExpanded()){
13269             return;
13270         }
13271         
13272         this.list.hide();
13273         
13274         if(this.tickable){
13275             this.hasFocus = false;
13276             this.okBtn.hide();
13277             this.cancelBtn.hide();
13278             this.trigger.show();
13279             
13280             if(this.editable){
13281                 this.tickableInputEl().dom.value = '';
13282                 this.tickableInputEl().blur();
13283             }
13284             
13285         }
13286         
13287         Roo.get(document).un('mousedown', this.collapseIf, this);
13288         Roo.get(document).un('mousewheel', this.collapseIf, this);
13289         if (!this.editable) {
13290             Roo.get(document).un('keydown', this.listKeyPress, this);
13291         }
13292         this.fireEvent('collapse', this);
13293     },
13294
13295     // private
13296     collapseIf : function(e){
13297         var in_combo  = e.within(this.el);
13298         var in_list =  e.within(this.list);
13299         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13300         
13301         if (in_combo || in_list || is_list) {
13302             //e.stopPropagation();
13303             return;
13304         }
13305         
13306         if(this.tickable){
13307             this.onTickableFooterButtonClick(e, false, false);
13308         }
13309
13310         this.collapse();
13311         
13312     },
13313
13314     /**
13315      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13316      */
13317     expand : function(){
13318        
13319         if(this.isExpanded() || !this.hasFocus){
13320             return;
13321         }
13322         
13323         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13324         this.list.setWidth(lw);
13325         
13326         
13327          Roo.log('expand');
13328         
13329         this.list.show();
13330         
13331         this.restrictHeight();
13332         
13333         if(this.tickable){
13334             
13335             this.tickItems = Roo.apply([], this.item);
13336             
13337             this.okBtn.show();
13338             this.cancelBtn.show();
13339             this.trigger.hide();
13340             
13341             if(this.editable){
13342                 this.tickableInputEl().focus();
13343             }
13344             
13345         }
13346         
13347         Roo.get(document).on('mousedown', this.collapseIf, this);
13348         Roo.get(document).on('mousewheel', this.collapseIf, this);
13349         if (!this.editable) {
13350             Roo.get(document).on('keydown', this.listKeyPress, this);
13351         }
13352         
13353         this.fireEvent('expand', this);
13354     },
13355
13356     // private
13357     // Implements the default empty TriggerField.onTriggerClick function
13358     onTriggerClick : function(e)
13359     {
13360         Roo.log('trigger click');
13361         
13362         if(this.disabled || !this.triggerList){
13363             return;
13364         }
13365         
13366         this.page = 0;
13367         this.loadNext = false;
13368         
13369         if(this.isExpanded()){
13370             this.collapse();
13371             if (!this.blockFocus) {
13372                 this.inputEl().focus();
13373             }
13374             
13375         }else {
13376             this.hasFocus = true;
13377             if(this.triggerAction == 'all') {
13378                 this.doQuery(this.allQuery, true);
13379             } else {
13380                 this.doQuery(this.getRawValue());
13381             }
13382             if (!this.blockFocus) {
13383                 this.inputEl().focus();
13384             }
13385         }
13386     },
13387     
13388     onTickableTriggerClick : function(e)
13389     {
13390         if(this.disabled){
13391             return;
13392         }
13393         
13394         this.page = 0;
13395         this.loadNext = false;
13396         this.hasFocus = true;
13397         
13398         if(this.triggerAction == 'all') {
13399             this.doQuery(this.allQuery, true);
13400         } else {
13401             this.doQuery(this.getRawValue());
13402         }
13403     },
13404     
13405     onSearchFieldClick : function(e)
13406     {
13407         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13408             this.onTickableFooterButtonClick(e, false, false);
13409             return;
13410         }
13411         
13412         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13413             return;
13414         }
13415         
13416         this.page = 0;
13417         this.loadNext = false;
13418         this.hasFocus = true;
13419         
13420         if(this.triggerAction == 'all') {
13421             this.doQuery(this.allQuery, true);
13422         } else {
13423             this.doQuery(this.getRawValue());
13424         }
13425     },
13426     
13427     listKeyPress : function(e)
13428     {
13429         //Roo.log('listkeypress');
13430         // scroll to first matching element based on key pres..
13431         if (e.isSpecialKey()) {
13432             return false;
13433         }
13434         var k = String.fromCharCode(e.getKey()).toUpperCase();
13435         //Roo.log(k);
13436         var match  = false;
13437         var csel = this.view.getSelectedNodes();
13438         var cselitem = false;
13439         if (csel.length) {
13440             var ix = this.view.indexOf(csel[0]);
13441             cselitem  = this.store.getAt(ix);
13442             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13443                 cselitem = false;
13444             }
13445             
13446         }
13447         
13448         this.store.each(function(v) { 
13449             if (cselitem) {
13450                 // start at existing selection.
13451                 if (cselitem.id == v.id) {
13452                     cselitem = false;
13453                 }
13454                 return true;
13455             }
13456                 
13457             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13458                 match = this.store.indexOf(v);
13459                 return false;
13460             }
13461             return true;
13462         }, this);
13463         
13464         if (match === false) {
13465             return true; // no more action?
13466         }
13467         // scroll to?
13468         this.view.select(match);
13469         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13470         sn.scrollIntoView(sn.dom.parentNode, false);
13471     },
13472     
13473     onViewScroll : function(e, t){
13474         
13475         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){
13476             return;
13477         }
13478         
13479         this.hasQuery = true;
13480         
13481         this.loading = this.list.select('.loading', true).first();
13482         
13483         if(this.loading === null){
13484             this.list.createChild({
13485                 tag: 'div',
13486                 cls: 'loading roo-select2-more-results roo-select2-active',
13487                 html: 'Loading more results...'
13488             });
13489             
13490             this.loading = this.list.select('.loading', true).first();
13491             
13492             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13493             
13494             this.loading.hide();
13495         }
13496         
13497         this.loading.show();
13498         
13499         var _combo = this;
13500         
13501         this.page++;
13502         this.loadNext = true;
13503         
13504         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13505         
13506         return;
13507     },
13508     
13509     addItem : function(o)
13510     {   
13511         var dv = ''; // display value
13512         
13513         if (this.displayField) {
13514             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13515         } else {
13516             // this is an error condition!!!
13517             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13518         }
13519         
13520         if(!dv.length){
13521             return;
13522         }
13523         
13524         var choice = this.choices.createChild({
13525             tag: 'li',
13526             cls: 'roo-select2-search-choice',
13527             cn: [
13528                 {
13529                     tag: 'div',
13530                     html: dv
13531                 },
13532                 {
13533                     tag: 'a',
13534                     href: '#',
13535                     cls: 'roo-select2-search-choice-close',
13536                     tabindex: '-1'
13537                 }
13538             ]
13539             
13540         }, this.searchField);
13541         
13542         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13543         
13544         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13545         
13546         this.item.push(o);
13547         
13548         this.lastData = o;
13549         
13550         this.syncValue();
13551         
13552         this.inputEl().dom.value = '';
13553         
13554         this.validate();
13555     },
13556     
13557     onRemoveItem : function(e, _self, o)
13558     {
13559         e.preventDefault();
13560         
13561         this.lastItem = Roo.apply([], this.item);
13562         
13563         var index = this.item.indexOf(o.data) * 1;
13564         
13565         if( index < 0){
13566             Roo.log('not this item?!');
13567             return;
13568         }
13569         
13570         this.item.splice(index, 1);
13571         o.item.remove();
13572         
13573         this.syncValue();
13574         
13575         this.fireEvent('remove', this, e);
13576         
13577         this.validate();
13578         
13579     },
13580     
13581     syncValue : function()
13582     {
13583         if(!this.item.length){
13584             this.clearValue();
13585             return;
13586         }
13587             
13588         var value = [];
13589         var _this = this;
13590         Roo.each(this.item, function(i){
13591             if(_this.valueField){
13592                 value.push(i[_this.valueField]);
13593                 return;
13594             }
13595
13596             value.push(i);
13597         });
13598
13599         this.value = value.join(',');
13600
13601         if(this.hiddenField){
13602             this.hiddenField.dom.value = this.value;
13603         }
13604         
13605         this.store.fireEvent("datachanged", this.store);
13606     },
13607     
13608     clearItem : function()
13609     {
13610         if(!this.multiple){
13611             return;
13612         }
13613         
13614         this.item = [];
13615         
13616         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13617            c.remove();
13618         });
13619         
13620         this.syncValue();
13621         
13622         this.validate();
13623         
13624         if(this.tickable && !Roo.isTouch){
13625             this.view.refresh();
13626         }
13627     },
13628     
13629     inputEl: function ()
13630     {
13631         if(Roo.isTouch && this.mobileTouchView){
13632             return this.el.select('input.form-control',true).first();
13633         }
13634         
13635         if(this.tickable){
13636             return this.searchField;
13637         }
13638         
13639         return this.el.select('input.form-control',true).first();
13640     },
13641     
13642     
13643     onTickableFooterButtonClick : function(e, btn, el)
13644     {
13645         e.preventDefault();
13646         
13647         this.lastItem = Roo.apply([], this.item);
13648         
13649         if(btn && btn.name == 'cancel'){
13650             this.tickItems = Roo.apply([], this.item);
13651             this.collapse();
13652             return;
13653         }
13654         
13655         this.clearItem();
13656         
13657         var _this = this;
13658         
13659         Roo.each(this.tickItems, function(o){
13660             _this.addItem(o);
13661         });
13662         
13663         this.collapse();
13664         
13665     },
13666     
13667     validate : function()
13668     {
13669         var v = this.getRawValue();
13670         
13671         if(this.multiple){
13672             v = this.getValue();
13673         }
13674         
13675         if(this.disabled || this.allowBlank || v.length){
13676             this.markValid();
13677             return true;
13678         }
13679         
13680         this.markInvalid();
13681         return false;
13682     },
13683     
13684     tickableInputEl : function()
13685     {
13686         if(!this.tickable || !this.editable){
13687             return this.inputEl();
13688         }
13689         
13690         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13691     },
13692     
13693     
13694     getAutoCreateTouchView : function()
13695     {
13696         var id = Roo.id();
13697         
13698         var cfg = {
13699             cls: 'form-group' //input-group
13700         };
13701         
13702         var input =  {
13703             tag: 'input',
13704             id : id,
13705             type : this.inputType,
13706             cls : 'form-control x-combo-noedit',
13707             autocomplete: 'new-password',
13708             placeholder : this.placeholder || '',
13709             readonly : true
13710         };
13711         
13712         if (this.name) {
13713             input.name = this.name;
13714         }
13715         
13716         if (this.size) {
13717             input.cls += ' input-' + this.size;
13718         }
13719         
13720         if (this.disabled) {
13721             input.disabled = true;
13722         }
13723         
13724         var inputblock = {
13725             cls : '',
13726             cn : [
13727                 input
13728             ]
13729         };
13730         
13731         if(this.before){
13732             inputblock.cls += ' input-group';
13733             
13734             inputblock.cn.unshift({
13735                 tag :'span',
13736                 cls : 'input-group-addon',
13737                 html : this.before
13738             });
13739         }
13740         
13741         if(this.removable && !this.multiple){
13742             inputblock.cls += ' roo-removable';
13743             
13744             inputblock.cn.push({
13745                 tag: 'button',
13746                 html : 'x',
13747                 cls : 'roo-combo-removable-btn close'
13748             });
13749         }
13750
13751         if(this.hasFeedback && !this.allowBlank){
13752             
13753             inputblock.cls += ' has-feedback';
13754             
13755             inputblock.cn.push({
13756                 tag: 'span',
13757                 cls: 'glyphicon form-control-feedback'
13758             });
13759             
13760         }
13761         
13762         if (this.after) {
13763             
13764             inputblock.cls += (this.before) ? '' : ' input-group';
13765             
13766             inputblock.cn.push({
13767                 tag :'span',
13768                 cls : 'input-group-addon',
13769                 html : this.after
13770             });
13771         }
13772
13773         var box = {
13774             tag: 'div',
13775             cn: [
13776                 {
13777                     tag: 'input',
13778                     type : 'hidden',
13779                     cls: 'form-hidden-field'
13780                 },
13781                 inputblock
13782             ]
13783             
13784         };
13785         
13786         if(this.multiple){
13787             box = {
13788                 tag: 'div',
13789                 cn: [
13790                     {
13791                         tag: 'input',
13792                         type : 'hidden',
13793                         cls: 'form-hidden-field'
13794                     },
13795                     {
13796                         tag: 'ul',
13797                         cls: 'roo-select2-choices',
13798                         cn:[
13799                             {
13800                                 tag: 'li',
13801                                 cls: 'roo-select2-search-field',
13802                                 cn: [
13803
13804                                     inputblock
13805                                 ]
13806                             }
13807                         ]
13808                     }
13809                 ]
13810             }
13811         };
13812         
13813         var combobox = {
13814             cls: 'roo-select2-container input-group',
13815             cn: [
13816                 box
13817             ]
13818         };
13819         
13820         if(this.multiple){
13821             combobox.cls += ' roo-select2-container-multi';
13822         }
13823         
13824         var align = this.labelAlign || this.parentLabelAlign();
13825         
13826         cfg.cn = combobox;
13827         
13828         if(this.fieldLabel.length){
13829             
13830             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13831             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13832             
13833             cfg.cn = [
13834                 {
13835                     tag: 'label',
13836                     cls : 'control-label ' + lw,
13837                     html : this.fieldLabel
13838
13839                 },
13840                 {
13841                     cls : cw, 
13842                     cn: [
13843                         combobox
13844                     ]
13845                 }
13846             ];
13847         }
13848         
13849         var settings = this;
13850         
13851         ['xs','sm','md','lg'].map(function(size){
13852             if (settings[size]) {
13853                 cfg.cls += ' col-' + size + '-' + settings[size];
13854             }
13855         });
13856         
13857         return cfg;
13858     },
13859     
13860     initTouchView : function()
13861     {
13862         this.renderTouchView();
13863         
13864         this.touchViewEl.on('scroll', function(){
13865             this.el.dom.scrollTop = 0;
13866         }, this);
13867         
13868         this.originalValue = this.getValue();
13869         
13870         this.inputEl().on("click", this.showTouchView, this);
13871         
13872         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13873         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13874         
13875         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13876         
13877         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13878         this.store.on('load', this.onTouchViewLoad, this);
13879         this.store.on('loadexception', this.onTouchViewLoadException, this);
13880         
13881         if(this.hiddenName){
13882             
13883             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13884             
13885             this.hiddenField.dom.value =
13886                 this.hiddenValue !== undefined ? this.hiddenValue :
13887                 this.value !== undefined ? this.value : '';
13888         
13889             this.el.dom.removeAttribute('name');
13890             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13891         }
13892         
13893         if(this.multiple){
13894             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13895             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13896         }
13897         
13898         if(this.removable && !this.multiple){
13899             var close = this.closeTriggerEl();
13900             if(close){
13901                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13902                 close.on('click', this.removeBtnClick, this, close);
13903             }
13904         }
13905         /*
13906          * fix the bug in Safari iOS8
13907          */
13908         this.inputEl().on("focus", function(e){
13909             document.activeElement.blur();
13910         }, this);
13911         
13912         return;
13913         
13914         
13915     },
13916     
13917     renderTouchView : function()
13918     {
13919         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13920         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13921         
13922         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13923         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13924         
13925         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13926         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13927         this.touchViewBodyEl.setStyle('overflow', 'auto');
13928         
13929         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13930         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13931         
13932         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13933         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13934         
13935     },
13936     
13937     showTouchView : function()
13938     {
13939         if(this.disabled){
13940             return;
13941         }
13942         
13943         this.touchViewHeaderEl.hide();
13944
13945         if(this.fieldLabel.length){
13946             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13947             this.touchViewHeaderEl.show();
13948         }
13949
13950         this.touchViewEl.show();
13951
13952         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13953         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13954
13955         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13956
13957         if(this.fieldLabel.length){
13958             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13959         }
13960         
13961         this.touchViewBodyEl.setHeight(bodyHeight);
13962
13963         if(this.animate){
13964             var _this = this;
13965             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13966         }else{
13967             this.touchViewEl.addClass('in');
13968         }
13969
13970         this.doTouchViewQuery();
13971         
13972     },
13973     
13974     hideTouchView : function()
13975     {
13976         this.touchViewEl.removeClass('in');
13977
13978         if(this.animate){
13979             var _this = this;
13980             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13981         }else{
13982             this.touchViewEl.setStyle('display', 'none');
13983         }
13984         
13985     },
13986     
13987     setTouchViewValue : function()
13988     {
13989         if(this.multiple){
13990             this.clearItem();
13991         
13992             var _this = this;
13993
13994             Roo.each(this.tickItems, function(o){
13995                 this.addItem(o);
13996             }, this);
13997         }
13998         
13999         this.hideTouchView();
14000     },
14001     
14002     doTouchViewQuery : function()
14003     {
14004         var qe = {
14005             query: '',
14006             forceAll: true,
14007             combo: this,
14008             cancel:false
14009         };
14010         
14011         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14012             return false;
14013         }
14014         
14015         if(!this.alwaysQuery || this.mode == 'local'){
14016             this.onTouchViewLoad();
14017             return;
14018         }
14019         
14020         this.store.load();
14021     },
14022     
14023     onTouchViewBeforeLoad : function(combo,opts)
14024     {
14025         return;
14026     },
14027
14028     // private
14029     onTouchViewLoad : function()
14030     {
14031         if(this.store.getCount() < 1){
14032             this.onTouchViewEmptyResults();
14033             return;
14034         }
14035         
14036         this.clearTouchView();
14037         
14038         var rawValue = this.getRawValue();
14039         
14040         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14041         
14042         this.tickItems = [];
14043         
14044         this.store.data.each(function(d, rowIndex){
14045             var row = this.touchViewListGroup.createChild(template);
14046             
14047             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14048                 row.addClass(d.data.cls);
14049             }
14050             
14051             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14052                 var cfg = {
14053                     data : d.data,
14054                     html : d.data[this.displayField]
14055                 };
14056                 
14057                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14058                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14059                 }
14060             }
14061             
14062             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14063                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14064             }
14065             
14066             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14067                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14068                 this.tickItems.push(d.data);
14069             }
14070             
14071             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14072             
14073         }, this);
14074         
14075         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14076         
14077         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14078
14079         if(this.fieldLabel.length){
14080             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14081         }
14082
14083         var listHeight = this.touchViewListGroup.getHeight();
14084         
14085         var _this = this;
14086         
14087         if(firstChecked && listHeight > bodyHeight){
14088             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14089         }
14090         
14091     },
14092     
14093     onTouchViewLoadException : function()
14094     {
14095         this.hideTouchView();
14096     },
14097     
14098     onTouchViewEmptyResults : function()
14099     {
14100         this.clearTouchView();
14101         
14102         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14103         
14104         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14105         
14106     },
14107     
14108     clearTouchView : function()
14109     {
14110         this.touchViewListGroup.dom.innerHTML = '';
14111     },
14112     
14113     onTouchViewClick : function(e, el, o)
14114     {
14115         e.preventDefault();
14116         
14117         var row = o.row;
14118         var rowIndex = o.rowIndex;
14119         
14120         var r = this.store.getAt(rowIndex);
14121         
14122         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14123             
14124             if(!this.multiple){
14125                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14126                     c.dom.removeAttribute('checked');
14127                 }, this);
14128
14129                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14130
14131                 this.setFromData(r.data);
14132
14133                 var close = this.closeTriggerEl();
14134
14135                 if(close){
14136                     close.show();
14137                 }
14138
14139                 this.hideTouchView();
14140
14141                 this.fireEvent('select', this, r, rowIndex);
14142
14143                 return;
14144             }
14145
14146             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14147                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14148                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14149                 return;
14150             }
14151
14152             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14153             this.addItem(r.data);
14154             this.tickItems.push(r.data);
14155         }
14156     }
14157     
14158
14159     /** 
14160     * @cfg {Boolean} grow 
14161     * @hide 
14162     */
14163     /** 
14164     * @cfg {Number} growMin 
14165     * @hide 
14166     */
14167     /** 
14168     * @cfg {Number} growMax 
14169     * @hide 
14170     */
14171     /**
14172      * @hide
14173      * @method autoSize
14174      */
14175 });
14176
14177 Roo.apply(Roo.bootstrap.ComboBox,  {
14178     
14179     header : {
14180         tag: 'div',
14181         cls: 'modal-header',
14182         cn: [
14183             {
14184                 tag: 'h4',
14185                 cls: 'modal-title'
14186             }
14187         ]
14188     },
14189     
14190     body : {
14191         tag: 'div',
14192         cls: 'modal-body',
14193         cn: [
14194             {
14195                 tag: 'ul',
14196                 cls: 'list-group'
14197             }
14198         ]
14199     },
14200     
14201     listItemRadio : {
14202         tag: 'li',
14203         cls: 'list-group-item',
14204         cn: [
14205             {
14206                 tag: 'span',
14207                 cls: 'roo-combobox-list-group-item-value'
14208             },
14209             {
14210                 tag: 'div',
14211                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14212                 cn: [
14213                     {
14214                         tag: 'input',
14215                         type: 'radio'
14216                     },
14217                     {
14218                         tag: 'label'
14219                     }
14220                 ]
14221             }
14222         ]
14223     },
14224     
14225     listItemCheckbox : {
14226         tag: 'li',
14227         cls: 'list-group-item',
14228         cn: [
14229             {
14230                 tag: 'span',
14231                 cls: 'roo-combobox-list-group-item-value'
14232             },
14233             {
14234                 tag: 'div',
14235                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14236                 cn: [
14237                     {
14238                         tag: 'input',
14239                         type: 'checkbox'
14240                     },
14241                     {
14242                         tag: 'label'
14243                     }
14244                 ]
14245             }
14246         ]
14247     },
14248     
14249     emptyResult : {
14250         tag: 'div',
14251         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14252     },
14253     
14254     footer : {
14255         tag: 'div',
14256         cls: 'modal-footer',
14257         cn: [
14258             {
14259                 tag: 'div',
14260                 cls: 'row',
14261                 cn: [
14262                     {
14263                         tag: 'div',
14264                         cls: 'col-xs-6 text-left',
14265                         cn: {
14266                             tag: 'button',
14267                             cls: 'btn btn-danger roo-touch-view-cancel',
14268                             html: 'Cancel'
14269                         }
14270                     },
14271                     {
14272                         tag: 'div',
14273                         cls: 'col-xs-6 text-right',
14274                         cn: {
14275                             tag: 'button',
14276                             cls: 'btn btn-success roo-touch-view-ok',
14277                             html: 'OK'
14278                         }
14279                     }
14280                 ]
14281             }
14282         ]
14283         
14284     }
14285 });
14286
14287 Roo.apply(Roo.bootstrap.ComboBox,  {
14288     
14289     touchViewTemplate : {
14290         tag: 'div',
14291         cls: 'modal fade roo-combobox-touch-view',
14292         cn: [
14293             {
14294                 tag: 'div',
14295                 cls: 'modal-dialog',
14296                 style : 'position:fixed', // we have to fix position....
14297                 cn: [
14298                     {
14299                         tag: 'div',
14300                         cls: 'modal-content',
14301                         cn: [
14302                             Roo.bootstrap.ComboBox.header,
14303                             Roo.bootstrap.ComboBox.body,
14304                             Roo.bootstrap.ComboBox.footer
14305                         ]
14306                     }
14307                 ]
14308             }
14309         ]
14310     }
14311 });/*
14312  * Based on:
14313  * Ext JS Library 1.1.1
14314  * Copyright(c) 2006-2007, Ext JS, LLC.
14315  *
14316  * Originally Released Under LGPL - original licence link has changed is not relivant.
14317  *
14318  * Fork - LGPL
14319  * <script type="text/javascript">
14320  */
14321
14322 /**
14323  * @class Roo.View
14324  * @extends Roo.util.Observable
14325  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14326  * This class also supports single and multi selection modes. <br>
14327  * Create a data model bound view:
14328  <pre><code>
14329  var store = new Roo.data.Store(...);
14330
14331  var view = new Roo.View({
14332     el : "my-element",
14333     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14334  
14335     singleSelect: true,
14336     selectedClass: "ydataview-selected",
14337     store: store
14338  });
14339
14340  // listen for node click?
14341  view.on("click", function(vw, index, node, e){
14342  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14343  });
14344
14345  // load XML data
14346  dataModel.load("foobar.xml");
14347  </code></pre>
14348  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14349  * <br><br>
14350  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14351  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14352  * 
14353  * Note: old style constructor is still suported (container, template, config)
14354  * 
14355  * @constructor
14356  * Create a new View
14357  * @param {Object} config The config object
14358  * 
14359  */
14360 Roo.View = function(config, depreciated_tpl, depreciated_config){
14361     
14362     this.parent = false;
14363     
14364     if (typeof(depreciated_tpl) == 'undefined') {
14365         // new way.. - universal constructor.
14366         Roo.apply(this, config);
14367         this.el  = Roo.get(this.el);
14368     } else {
14369         // old format..
14370         this.el  = Roo.get(config);
14371         this.tpl = depreciated_tpl;
14372         Roo.apply(this, depreciated_config);
14373     }
14374     this.wrapEl  = this.el.wrap().wrap();
14375     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14376     
14377     
14378     if(typeof(this.tpl) == "string"){
14379         this.tpl = new Roo.Template(this.tpl);
14380     } else {
14381         // support xtype ctors..
14382         this.tpl = new Roo.factory(this.tpl, Roo);
14383     }
14384     
14385     
14386     this.tpl.compile();
14387     
14388     /** @private */
14389     this.addEvents({
14390         /**
14391          * @event beforeclick
14392          * Fires before a click is processed. Returns false to cancel the default action.
14393          * @param {Roo.View} this
14394          * @param {Number} index The index of the target node
14395          * @param {HTMLElement} node The target node
14396          * @param {Roo.EventObject} e The raw event object
14397          */
14398             "beforeclick" : true,
14399         /**
14400          * @event click
14401          * Fires when a template node is clicked.
14402          * @param {Roo.View} this
14403          * @param {Number} index The index of the target node
14404          * @param {HTMLElement} node The target node
14405          * @param {Roo.EventObject} e The raw event object
14406          */
14407             "click" : true,
14408         /**
14409          * @event dblclick
14410          * Fires when a template node is double clicked.
14411          * @param {Roo.View} this
14412          * @param {Number} index The index of the target node
14413          * @param {HTMLElement} node The target node
14414          * @param {Roo.EventObject} e The raw event object
14415          */
14416             "dblclick" : true,
14417         /**
14418          * @event contextmenu
14419          * Fires when a template node is right clicked.
14420          * @param {Roo.View} this
14421          * @param {Number} index The index of the target node
14422          * @param {HTMLElement} node The target node
14423          * @param {Roo.EventObject} e The raw event object
14424          */
14425             "contextmenu" : true,
14426         /**
14427          * @event selectionchange
14428          * Fires when the selected nodes change.
14429          * @param {Roo.View} this
14430          * @param {Array} selections Array of the selected nodes
14431          */
14432             "selectionchange" : true,
14433     
14434         /**
14435          * @event beforeselect
14436          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14437          * @param {Roo.View} this
14438          * @param {HTMLElement} node The node to be selected
14439          * @param {Array} selections Array of currently selected nodes
14440          */
14441             "beforeselect" : true,
14442         /**
14443          * @event preparedata
14444          * Fires on every row to render, to allow you to change the data.
14445          * @param {Roo.View} this
14446          * @param {Object} data to be rendered (change this)
14447          */
14448           "preparedata" : true
14449           
14450           
14451         });
14452
14453
14454
14455     this.el.on({
14456         "click": this.onClick,
14457         "dblclick": this.onDblClick,
14458         "contextmenu": this.onContextMenu,
14459         scope:this
14460     });
14461
14462     this.selections = [];
14463     this.nodes = [];
14464     this.cmp = new Roo.CompositeElementLite([]);
14465     if(this.store){
14466         this.store = Roo.factory(this.store, Roo.data);
14467         this.setStore(this.store, true);
14468     }
14469     
14470     if ( this.footer && this.footer.xtype) {
14471            
14472          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14473         
14474         this.footer.dataSource = this.store;
14475         this.footer.container = fctr;
14476         this.footer = Roo.factory(this.footer, Roo);
14477         fctr.insertFirst(this.el);
14478         
14479         // this is a bit insane - as the paging toolbar seems to detach the el..
14480 //        dom.parentNode.parentNode.parentNode
14481          // they get detached?
14482     }
14483     
14484     
14485     Roo.View.superclass.constructor.call(this);
14486     
14487     
14488 };
14489
14490 Roo.extend(Roo.View, Roo.util.Observable, {
14491     
14492      /**
14493      * @cfg {Roo.data.Store} store Data store to load data from.
14494      */
14495     store : false,
14496     
14497     /**
14498      * @cfg {String|Roo.Element} el The container element.
14499      */
14500     el : '',
14501     
14502     /**
14503      * @cfg {String|Roo.Template} tpl The template used by this View 
14504      */
14505     tpl : false,
14506     /**
14507      * @cfg {String} dataName the named area of the template to use as the data area
14508      *                          Works with domtemplates roo-name="name"
14509      */
14510     dataName: false,
14511     /**
14512      * @cfg {String} selectedClass The css class to add to selected nodes
14513      */
14514     selectedClass : "x-view-selected",
14515      /**
14516      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14517      */
14518     emptyText : "",
14519     
14520     /**
14521      * @cfg {String} text to display on mask (default Loading)
14522      */
14523     mask : false,
14524     /**
14525      * @cfg {Boolean} multiSelect Allow multiple selection
14526      */
14527     multiSelect : false,
14528     /**
14529      * @cfg {Boolean} singleSelect Allow single selection
14530      */
14531     singleSelect:  false,
14532     
14533     /**
14534      * @cfg {Boolean} toggleSelect - selecting 
14535      */
14536     toggleSelect : false,
14537     
14538     /**
14539      * @cfg {Boolean} tickable - selecting 
14540      */
14541     tickable : false,
14542     
14543     /**
14544      * Returns the element this view is bound to.
14545      * @return {Roo.Element}
14546      */
14547     getEl : function(){
14548         return this.wrapEl;
14549     },
14550     
14551     
14552
14553     /**
14554      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14555      */
14556     refresh : function(){
14557         //Roo.log('refresh');
14558         var t = this.tpl;
14559         
14560         // if we are using something like 'domtemplate', then
14561         // the what gets used is:
14562         // t.applySubtemplate(NAME, data, wrapping data..)
14563         // the outer template then get' applied with
14564         //     the store 'extra data'
14565         // and the body get's added to the
14566         //      roo-name="data" node?
14567         //      <span class='roo-tpl-{name}'></span> ?????
14568         
14569         
14570         
14571         this.clearSelections();
14572         this.el.update("");
14573         var html = [];
14574         var records = this.store.getRange();
14575         if(records.length < 1) {
14576             
14577             // is this valid??  = should it render a template??
14578             
14579             this.el.update(this.emptyText);
14580             return;
14581         }
14582         var el = this.el;
14583         if (this.dataName) {
14584             this.el.update(t.apply(this.store.meta)); //????
14585             el = this.el.child('.roo-tpl-' + this.dataName);
14586         }
14587         
14588         for(var i = 0, len = records.length; i < len; i++){
14589             var data = this.prepareData(records[i].data, i, records[i]);
14590             this.fireEvent("preparedata", this, data, i, records[i]);
14591             
14592             var d = Roo.apply({}, data);
14593             
14594             if(this.tickable){
14595                 Roo.apply(d, {'roo-id' : Roo.id()});
14596                 
14597                 var _this = this;
14598             
14599                 Roo.each(this.parent.item, function(item){
14600                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14601                         return;
14602                     }
14603                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14604                 });
14605             }
14606             
14607             html[html.length] = Roo.util.Format.trim(
14608                 this.dataName ?
14609                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14610                     t.apply(d)
14611             );
14612         }
14613         
14614         
14615         
14616         el.update(html.join(""));
14617         this.nodes = el.dom.childNodes;
14618         this.updateIndexes(0);
14619     },
14620     
14621
14622     /**
14623      * Function to override to reformat the data that is sent to
14624      * the template for each node.
14625      * DEPRICATED - use the preparedata event handler.
14626      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14627      * a JSON object for an UpdateManager bound view).
14628      */
14629     prepareData : function(data, index, record)
14630     {
14631         this.fireEvent("preparedata", this, data, index, record);
14632         return data;
14633     },
14634
14635     onUpdate : function(ds, record){
14636         // Roo.log('on update');   
14637         this.clearSelections();
14638         var index = this.store.indexOf(record);
14639         var n = this.nodes[index];
14640         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14641         n.parentNode.removeChild(n);
14642         this.updateIndexes(index, index);
14643     },
14644
14645     
14646     
14647 // --------- FIXME     
14648     onAdd : function(ds, records, index)
14649     {
14650         //Roo.log(['on Add', ds, records, index] );        
14651         this.clearSelections();
14652         if(this.nodes.length == 0){
14653             this.refresh();
14654             return;
14655         }
14656         var n = this.nodes[index];
14657         for(var i = 0, len = records.length; i < len; i++){
14658             var d = this.prepareData(records[i].data, i, records[i]);
14659             if(n){
14660                 this.tpl.insertBefore(n, d);
14661             }else{
14662                 
14663                 this.tpl.append(this.el, d);
14664             }
14665         }
14666         this.updateIndexes(index);
14667     },
14668
14669     onRemove : function(ds, record, index){
14670        // Roo.log('onRemove');
14671         this.clearSelections();
14672         var el = this.dataName  ?
14673             this.el.child('.roo-tpl-' + this.dataName) :
14674             this.el; 
14675         
14676         el.dom.removeChild(this.nodes[index]);
14677         this.updateIndexes(index);
14678     },
14679
14680     /**
14681      * Refresh an individual node.
14682      * @param {Number} index
14683      */
14684     refreshNode : function(index){
14685         this.onUpdate(this.store, this.store.getAt(index));
14686     },
14687
14688     updateIndexes : function(startIndex, endIndex){
14689         var ns = this.nodes;
14690         startIndex = startIndex || 0;
14691         endIndex = endIndex || ns.length - 1;
14692         for(var i = startIndex; i <= endIndex; i++){
14693             ns[i].nodeIndex = i;
14694         }
14695     },
14696
14697     /**
14698      * Changes the data store this view uses and refresh the view.
14699      * @param {Store} store
14700      */
14701     setStore : function(store, initial){
14702         if(!initial && this.store){
14703             this.store.un("datachanged", this.refresh);
14704             this.store.un("add", this.onAdd);
14705             this.store.un("remove", this.onRemove);
14706             this.store.un("update", this.onUpdate);
14707             this.store.un("clear", this.refresh);
14708             this.store.un("beforeload", this.onBeforeLoad);
14709             this.store.un("load", this.onLoad);
14710             this.store.un("loadexception", this.onLoad);
14711         }
14712         if(store){
14713           
14714             store.on("datachanged", this.refresh, this);
14715             store.on("add", this.onAdd, this);
14716             store.on("remove", this.onRemove, this);
14717             store.on("update", this.onUpdate, this);
14718             store.on("clear", this.refresh, this);
14719             store.on("beforeload", this.onBeforeLoad, this);
14720             store.on("load", this.onLoad, this);
14721             store.on("loadexception", this.onLoad, this);
14722         }
14723         
14724         if(store){
14725             this.refresh();
14726         }
14727     },
14728     /**
14729      * onbeforeLoad - masks the loading area.
14730      *
14731      */
14732     onBeforeLoad : function(store,opts)
14733     {
14734          //Roo.log('onBeforeLoad');   
14735         if (!opts.add) {
14736             this.el.update("");
14737         }
14738         this.el.mask(this.mask ? this.mask : "Loading" ); 
14739     },
14740     onLoad : function ()
14741     {
14742         this.el.unmask();
14743     },
14744     
14745
14746     /**
14747      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14748      * @param {HTMLElement} node
14749      * @return {HTMLElement} The template node
14750      */
14751     findItemFromChild : function(node){
14752         var el = this.dataName  ?
14753             this.el.child('.roo-tpl-' + this.dataName,true) :
14754             this.el.dom; 
14755         
14756         if(!node || node.parentNode == el){
14757                     return node;
14758             }
14759             var p = node.parentNode;
14760             while(p && p != el){
14761             if(p.parentNode == el){
14762                 return p;
14763             }
14764             p = p.parentNode;
14765         }
14766             return null;
14767     },
14768
14769     /** @ignore */
14770     onClick : function(e){
14771         var item = this.findItemFromChild(e.getTarget());
14772         if(item){
14773             var index = this.indexOf(item);
14774             if(this.onItemClick(item, index, e) !== false){
14775                 this.fireEvent("click", this, index, item, e);
14776             }
14777         }else{
14778             this.clearSelections();
14779         }
14780     },
14781
14782     /** @ignore */
14783     onContextMenu : function(e){
14784         var item = this.findItemFromChild(e.getTarget());
14785         if(item){
14786             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14787         }
14788     },
14789
14790     /** @ignore */
14791     onDblClick : function(e){
14792         var item = this.findItemFromChild(e.getTarget());
14793         if(item){
14794             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14795         }
14796     },
14797
14798     onItemClick : function(item, index, e)
14799     {
14800         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14801             return false;
14802         }
14803         if (this.toggleSelect) {
14804             var m = this.isSelected(item) ? 'unselect' : 'select';
14805             //Roo.log(m);
14806             var _t = this;
14807             _t[m](item, true, false);
14808             return true;
14809         }
14810         if(this.multiSelect || this.singleSelect){
14811             if(this.multiSelect && e.shiftKey && this.lastSelection){
14812                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14813             }else{
14814                 this.select(item, this.multiSelect && e.ctrlKey);
14815                 this.lastSelection = item;
14816             }
14817             
14818             if(!this.tickable){
14819                 e.preventDefault();
14820             }
14821             
14822         }
14823         return true;
14824     },
14825
14826     /**
14827      * Get the number of selected nodes.
14828      * @return {Number}
14829      */
14830     getSelectionCount : function(){
14831         return this.selections.length;
14832     },
14833
14834     /**
14835      * Get the currently selected nodes.
14836      * @return {Array} An array of HTMLElements
14837      */
14838     getSelectedNodes : function(){
14839         return this.selections;
14840     },
14841
14842     /**
14843      * Get the indexes of the selected nodes.
14844      * @return {Array}
14845      */
14846     getSelectedIndexes : function(){
14847         var indexes = [], s = this.selections;
14848         for(var i = 0, len = s.length; i < len; i++){
14849             indexes.push(s[i].nodeIndex);
14850         }
14851         return indexes;
14852     },
14853
14854     /**
14855      * Clear all selections
14856      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14857      */
14858     clearSelections : function(suppressEvent){
14859         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14860             this.cmp.elements = this.selections;
14861             this.cmp.removeClass(this.selectedClass);
14862             this.selections = [];
14863             if(!suppressEvent){
14864                 this.fireEvent("selectionchange", this, this.selections);
14865             }
14866         }
14867     },
14868
14869     /**
14870      * Returns true if the passed node is selected
14871      * @param {HTMLElement/Number} node The node or node index
14872      * @return {Boolean}
14873      */
14874     isSelected : function(node){
14875         var s = this.selections;
14876         if(s.length < 1){
14877             return false;
14878         }
14879         node = this.getNode(node);
14880         return s.indexOf(node) !== -1;
14881     },
14882
14883     /**
14884      * Selects nodes.
14885      * @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
14886      * @param {Boolean} keepExisting (optional) true to keep existing selections
14887      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14888      */
14889     select : function(nodeInfo, keepExisting, suppressEvent){
14890         if(nodeInfo instanceof Array){
14891             if(!keepExisting){
14892                 this.clearSelections(true);
14893             }
14894             for(var i = 0, len = nodeInfo.length; i < len; i++){
14895                 this.select(nodeInfo[i], true, true);
14896             }
14897             return;
14898         } 
14899         var node = this.getNode(nodeInfo);
14900         if(!node || this.isSelected(node)){
14901             return; // already selected.
14902         }
14903         if(!keepExisting){
14904             this.clearSelections(true);
14905         }
14906         
14907         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14908             Roo.fly(node).addClass(this.selectedClass);
14909             this.selections.push(node);
14910             if(!suppressEvent){
14911                 this.fireEvent("selectionchange", this, this.selections);
14912             }
14913         }
14914         
14915         
14916     },
14917       /**
14918      * Unselects nodes.
14919      * @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
14920      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14921      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14922      */
14923     unselect : function(nodeInfo, keepExisting, suppressEvent)
14924     {
14925         if(nodeInfo instanceof Array){
14926             Roo.each(this.selections, function(s) {
14927                 this.unselect(s, nodeInfo);
14928             }, this);
14929             return;
14930         }
14931         var node = this.getNode(nodeInfo);
14932         if(!node || !this.isSelected(node)){
14933             //Roo.log("not selected");
14934             return; // not selected.
14935         }
14936         // fireevent???
14937         var ns = [];
14938         Roo.each(this.selections, function(s) {
14939             if (s == node ) {
14940                 Roo.fly(node).removeClass(this.selectedClass);
14941
14942                 return;
14943             }
14944             ns.push(s);
14945         },this);
14946         
14947         this.selections= ns;
14948         this.fireEvent("selectionchange", this, this.selections);
14949     },
14950
14951     /**
14952      * Gets a template node.
14953      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14954      * @return {HTMLElement} The node or null if it wasn't found
14955      */
14956     getNode : function(nodeInfo){
14957         if(typeof nodeInfo == "string"){
14958             return document.getElementById(nodeInfo);
14959         }else if(typeof nodeInfo == "number"){
14960             return this.nodes[nodeInfo];
14961         }
14962         return nodeInfo;
14963     },
14964
14965     /**
14966      * Gets a range template nodes.
14967      * @param {Number} startIndex
14968      * @param {Number} endIndex
14969      * @return {Array} An array of nodes
14970      */
14971     getNodes : function(start, end){
14972         var ns = this.nodes;
14973         start = start || 0;
14974         end = typeof end == "undefined" ? ns.length - 1 : end;
14975         var nodes = [];
14976         if(start <= end){
14977             for(var i = start; i <= end; i++){
14978                 nodes.push(ns[i]);
14979             }
14980         } else{
14981             for(var i = start; i >= end; i--){
14982                 nodes.push(ns[i]);
14983             }
14984         }
14985         return nodes;
14986     },
14987
14988     /**
14989      * Finds the index of the passed node
14990      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14991      * @return {Number} The index of the node or -1
14992      */
14993     indexOf : function(node){
14994         node = this.getNode(node);
14995         if(typeof node.nodeIndex == "number"){
14996             return node.nodeIndex;
14997         }
14998         var ns = this.nodes;
14999         for(var i = 0, len = ns.length; i < len; i++){
15000             if(ns[i] == node){
15001                 return i;
15002             }
15003         }
15004         return -1;
15005     }
15006 });
15007 /*
15008  * - LGPL
15009  *
15010  * based on jquery fullcalendar
15011  * 
15012  */
15013
15014 Roo.bootstrap = Roo.bootstrap || {};
15015 /**
15016  * @class Roo.bootstrap.Calendar
15017  * @extends Roo.bootstrap.Component
15018  * Bootstrap Calendar class
15019  * @cfg {Boolean} loadMask (true|false) default false
15020  * @cfg {Object} header generate the user specific header of the calendar, default false
15021
15022  * @constructor
15023  * Create a new Container
15024  * @param {Object} config The config object
15025  */
15026
15027
15028
15029 Roo.bootstrap.Calendar = function(config){
15030     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15031      this.addEvents({
15032         /**
15033              * @event select
15034              * Fires when a date is selected
15035              * @param {DatePicker} this
15036              * @param {Date} date The selected date
15037              */
15038         'select': true,
15039         /**
15040              * @event monthchange
15041              * Fires when the displayed month changes 
15042              * @param {DatePicker} this
15043              * @param {Date} date The selected month
15044              */
15045         'monthchange': true,
15046         /**
15047              * @event evententer
15048              * Fires when mouse over an event
15049              * @param {Calendar} this
15050              * @param {event} Event
15051              */
15052         'evententer': true,
15053         /**
15054              * @event eventleave
15055              * Fires when the mouse leaves an
15056              * @param {Calendar} this
15057              * @param {event}
15058              */
15059         'eventleave': true,
15060         /**
15061              * @event eventclick
15062              * Fires when the mouse click an
15063              * @param {Calendar} this
15064              * @param {event}
15065              */
15066         'eventclick': true
15067         
15068     });
15069
15070 };
15071
15072 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15073     
15074      /**
15075      * @cfg {Number} startDay
15076      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15077      */
15078     startDay : 0,
15079     
15080     loadMask : false,
15081     
15082     header : false,
15083       
15084     getAutoCreate : function(){
15085         
15086         
15087         var fc_button = function(name, corner, style, content ) {
15088             return Roo.apply({},{
15089                 tag : 'span',
15090                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15091                          (corner.length ?
15092                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15093                             ''
15094                         ),
15095                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15096                 unselectable: 'on'
15097             });
15098         };
15099         
15100         var header = {};
15101         
15102         if(!this.header){
15103             header = {
15104                 tag : 'table',
15105                 cls : 'fc-header',
15106                 style : 'width:100%',
15107                 cn : [
15108                     {
15109                         tag: 'tr',
15110                         cn : [
15111                             {
15112                                 tag : 'td',
15113                                 cls : 'fc-header-left',
15114                                 cn : [
15115                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15116                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15117                                     { tag: 'span', cls: 'fc-header-space' },
15118                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15119
15120
15121                                 ]
15122                             },
15123
15124                             {
15125                                 tag : 'td',
15126                                 cls : 'fc-header-center',
15127                                 cn : [
15128                                     {
15129                                         tag: 'span',
15130                                         cls: 'fc-header-title',
15131                                         cn : {
15132                                             tag: 'H2',
15133                                             html : 'month / year'
15134                                         }
15135                                     }
15136
15137                                 ]
15138                             },
15139                             {
15140                                 tag : 'td',
15141                                 cls : 'fc-header-right',
15142                                 cn : [
15143                               /*      fc_button('month', 'left', '', 'month' ),
15144                                     fc_button('week', '', '', 'week' ),
15145                                     fc_button('day', 'right', '', 'day' )
15146                                 */    
15147
15148                                 ]
15149                             }
15150
15151                         ]
15152                     }
15153                 ]
15154             };
15155         }
15156         
15157         header = this.header;
15158         
15159        
15160         var cal_heads = function() {
15161             var ret = [];
15162             // fixme - handle this.
15163             
15164             for (var i =0; i < Date.dayNames.length; i++) {
15165                 var d = Date.dayNames[i];
15166                 ret.push({
15167                     tag: 'th',
15168                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15169                     html : d.substring(0,3)
15170                 });
15171                 
15172             }
15173             ret[0].cls += ' fc-first';
15174             ret[6].cls += ' fc-last';
15175             return ret;
15176         };
15177         var cal_cell = function(n) {
15178             return  {
15179                 tag: 'td',
15180                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15181                 cn : [
15182                     {
15183                         cn : [
15184                             {
15185                                 cls: 'fc-day-number',
15186                                 html: 'D'
15187                             },
15188                             {
15189                                 cls: 'fc-day-content',
15190                              
15191                                 cn : [
15192                                      {
15193                                         style: 'position: relative;' // height: 17px;
15194                                     }
15195                                 ]
15196                             }
15197                             
15198                             
15199                         ]
15200                     }
15201                 ]
15202                 
15203             }
15204         };
15205         var cal_rows = function() {
15206             
15207             var ret = [];
15208             for (var r = 0; r < 6; r++) {
15209                 var row= {
15210                     tag : 'tr',
15211                     cls : 'fc-week',
15212                     cn : []
15213                 };
15214                 
15215                 for (var i =0; i < Date.dayNames.length; i++) {
15216                     var d = Date.dayNames[i];
15217                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15218
15219                 }
15220                 row.cn[0].cls+=' fc-first';
15221                 row.cn[0].cn[0].style = 'min-height:90px';
15222                 row.cn[6].cls+=' fc-last';
15223                 ret.push(row);
15224                 
15225             }
15226             ret[0].cls += ' fc-first';
15227             ret[4].cls += ' fc-prev-last';
15228             ret[5].cls += ' fc-last';
15229             return ret;
15230             
15231         };
15232         
15233         var cal_table = {
15234             tag: 'table',
15235             cls: 'fc-border-separate',
15236             style : 'width:100%',
15237             cellspacing  : 0,
15238             cn : [
15239                 { 
15240                     tag: 'thead',
15241                     cn : [
15242                         { 
15243                             tag: 'tr',
15244                             cls : 'fc-first fc-last',
15245                             cn : cal_heads()
15246                         }
15247                     ]
15248                 },
15249                 { 
15250                     tag: 'tbody',
15251                     cn : cal_rows()
15252                 }
15253                   
15254             ]
15255         };
15256          
15257          var cfg = {
15258             cls : 'fc fc-ltr',
15259             cn : [
15260                 header,
15261                 {
15262                     cls : 'fc-content',
15263                     style : "position: relative;",
15264                     cn : [
15265                         {
15266                             cls : 'fc-view fc-view-month fc-grid',
15267                             style : 'position: relative',
15268                             unselectable : 'on',
15269                             cn : [
15270                                 {
15271                                     cls : 'fc-event-container',
15272                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15273                                 },
15274                                 cal_table
15275                             ]
15276                         }
15277                     ]
15278     
15279                 }
15280            ] 
15281             
15282         };
15283         
15284          
15285         
15286         return cfg;
15287     },
15288     
15289     
15290     initEvents : function()
15291     {
15292         if(!this.store){
15293             throw "can not find store for calendar";
15294         }
15295         
15296         var mark = {
15297             tag: "div",
15298             cls:"x-dlg-mask",
15299             style: "text-align:center",
15300             cn: [
15301                 {
15302                     tag: "div",
15303                     style: "background-color:white;width:50%;margin:250 auto",
15304                     cn: [
15305                         {
15306                             tag: "img",
15307                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15308                         },
15309                         {
15310                             tag: "span",
15311                             html: "Loading"
15312                         }
15313                         
15314                     ]
15315                 }
15316             ]
15317         };
15318         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15319         
15320         var size = this.el.select('.fc-content', true).first().getSize();
15321         this.maskEl.setSize(size.width, size.height);
15322         this.maskEl.enableDisplayMode("block");
15323         if(!this.loadMask){
15324             this.maskEl.hide();
15325         }
15326         
15327         this.store = Roo.factory(this.store, Roo.data);
15328         this.store.on('load', this.onLoad, this);
15329         this.store.on('beforeload', this.onBeforeLoad, this);
15330         
15331         this.resize();
15332         
15333         this.cells = this.el.select('.fc-day',true);
15334         //Roo.log(this.cells);
15335         this.textNodes = this.el.query('.fc-day-number');
15336         this.cells.addClassOnOver('fc-state-hover');
15337         
15338         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15339         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15340         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15341         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15342         
15343         this.on('monthchange', this.onMonthChange, this);
15344         
15345         this.update(new Date().clearTime());
15346     },
15347     
15348     resize : function() {
15349         var sz  = this.el.getSize();
15350         
15351         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15352         this.el.select('.fc-day-content div',true).setHeight(34);
15353     },
15354     
15355     
15356     // private
15357     showPrevMonth : function(e){
15358         this.update(this.activeDate.add("mo", -1));
15359     },
15360     showToday : function(e){
15361         this.update(new Date().clearTime());
15362     },
15363     // private
15364     showNextMonth : function(e){
15365         this.update(this.activeDate.add("mo", 1));
15366     },
15367
15368     // private
15369     showPrevYear : function(){
15370         this.update(this.activeDate.add("y", -1));
15371     },
15372
15373     // private
15374     showNextYear : function(){
15375         this.update(this.activeDate.add("y", 1));
15376     },
15377
15378     
15379    // private
15380     update : function(date)
15381     {
15382         var vd = this.activeDate;
15383         this.activeDate = date;
15384 //        if(vd && this.el){
15385 //            var t = date.getTime();
15386 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15387 //                Roo.log('using add remove');
15388 //                
15389 //                this.fireEvent('monthchange', this, date);
15390 //                
15391 //                this.cells.removeClass("fc-state-highlight");
15392 //                this.cells.each(function(c){
15393 //                   if(c.dateValue == t){
15394 //                       c.addClass("fc-state-highlight");
15395 //                       setTimeout(function(){
15396 //                            try{c.dom.firstChild.focus();}catch(e){}
15397 //                       }, 50);
15398 //                       return false;
15399 //                   }
15400 //                   return true;
15401 //                });
15402 //                return;
15403 //            }
15404 //        }
15405         
15406         var days = date.getDaysInMonth();
15407         
15408         var firstOfMonth = date.getFirstDateOfMonth();
15409         var startingPos = firstOfMonth.getDay()-this.startDay;
15410         
15411         if(startingPos < this.startDay){
15412             startingPos += 7;
15413         }
15414         
15415         var pm = date.add(Date.MONTH, -1);
15416         var prevStart = pm.getDaysInMonth()-startingPos;
15417 //        
15418         this.cells = this.el.select('.fc-day',true);
15419         this.textNodes = this.el.query('.fc-day-number');
15420         this.cells.addClassOnOver('fc-state-hover');
15421         
15422         var cells = this.cells.elements;
15423         var textEls = this.textNodes;
15424         
15425         Roo.each(cells, function(cell){
15426             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15427         });
15428         
15429         days += startingPos;
15430
15431         // convert everything to numbers so it's fast
15432         var day = 86400000;
15433         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15434         //Roo.log(d);
15435         //Roo.log(pm);
15436         //Roo.log(prevStart);
15437         
15438         var today = new Date().clearTime().getTime();
15439         var sel = date.clearTime().getTime();
15440         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15441         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15442         var ddMatch = this.disabledDatesRE;
15443         var ddText = this.disabledDatesText;
15444         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15445         var ddaysText = this.disabledDaysText;
15446         var format = this.format;
15447         
15448         var setCellClass = function(cal, cell){
15449             cell.row = 0;
15450             cell.events = [];
15451             cell.more = [];
15452             //Roo.log('set Cell Class');
15453             cell.title = "";
15454             var t = d.getTime();
15455             
15456             //Roo.log(d);
15457             
15458             cell.dateValue = t;
15459             if(t == today){
15460                 cell.className += " fc-today";
15461                 cell.className += " fc-state-highlight";
15462                 cell.title = cal.todayText;
15463             }
15464             if(t == sel){
15465                 // disable highlight in other month..
15466                 //cell.className += " fc-state-highlight";
15467                 
15468             }
15469             // disabling
15470             if(t < min) {
15471                 cell.className = " fc-state-disabled";
15472                 cell.title = cal.minText;
15473                 return;
15474             }
15475             if(t > max) {
15476                 cell.className = " fc-state-disabled";
15477                 cell.title = cal.maxText;
15478                 return;
15479             }
15480             if(ddays){
15481                 if(ddays.indexOf(d.getDay()) != -1){
15482                     cell.title = ddaysText;
15483                     cell.className = " fc-state-disabled";
15484                 }
15485             }
15486             if(ddMatch && format){
15487                 var fvalue = d.dateFormat(format);
15488                 if(ddMatch.test(fvalue)){
15489                     cell.title = ddText.replace("%0", fvalue);
15490                     cell.className = " fc-state-disabled";
15491                 }
15492             }
15493             
15494             if (!cell.initialClassName) {
15495                 cell.initialClassName = cell.dom.className;
15496             }
15497             
15498             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15499         };
15500
15501         var i = 0;
15502         
15503         for(; i < startingPos; i++) {
15504             textEls[i].innerHTML = (++prevStart);
15505             d.setDate(d.getDate()+1);
15506             
15507             cells[i].className = "fc-past fc-other-month";
15508             setCellClass(this, cells[i]);
15509         }
15510         
15511         var intDay = 0;
15512         
15513         for(; i < days; i++){
15514             intDay = i - startingPos + 1;
15515             textEls[i].innerHTML = (intDay);
15516             d.setDate(d.getDate()+1);
15517             
15518             cells[i].className = ''; // "x-date-active";
15519             setCellClass(this, cells[i]);
15520         }
15521         var extraDays = 0;
15522         
15523         for(; i < 42; i++) {
15524             textEls[i].innerHTML = (++extraDays);
15525             d.setDate(d.getDate()+1);
15526             
15527             cells[i].className = "fc-future fc-other-month";
15528             setCellClass(this, cells[i]);
15529         }
15530         
15531         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15532         
15533         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15534         
15535         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15536         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15537         
15538         if(totalRows != 6){
15539             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15540             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15541         }
15542         
15543         this.fireEvent('monthchange', this, date);
15544         
15545         
15546         /*
15547         if(!this.internalRender){
15548             var main = this.el.dom.firstChild;
15549             var w = main.offsetWidth;
15550             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15551             Roo.fly(main).setWidth(w);
15552             this.internalRender = true;
15553             // opera does not respect the auto grow header center column
15554             // then, after it gets a width opera refuses to recalculate
15555             // without a second pass
15556             if(Roo.isOpera && !this.secondPass){
15557                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15558                 this.secondPass = true;
15559                 this.update.defer(10, this, [date]);
15560             }
15561         }
15562         */
15563         
15564     },
15565     
15566     findCell : function(dt) {
15567         dt = dt.clearTime().getTime();
15568         var ret = false;
15569         this.cells.each(function(c){
15570             //Roo.log("check " +c.dateValue + '?=' + dt);
15571             if(c.dateValue == dt){
15572                 ret = c;
15573                 return false;
15574             }
15575             return true;
15576         });
15577         
15578         return ret;
15579     },
15580     
15581     findCells : function(ev) {
15582         var s = ev.start.clone().clearTime().getTime();
15583        // Roo.log(s);
15584         var e= ev.end.clone().clearTime().getTime();
15585        // Roo.log(e);
15586         var ret = [];
15587         this.cells.each(function(c){
15588              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15589             
15590             if(c.dateValue > e){
15591                 return ;
15592             }
15593             if(c.dateValue < s){
15594                 return ;
15595             }
15596             ret.push(c);
15597         });
15598         
15599         return ret;    
15600     },
15601     
15602 //    findBestRow: function(cells)
15603 //    {
15604 //        var ret = 0;
15605 //        
15606 //        for (var i =0 ; i < cells.length;i++) {
15607 //            ret  = Math.max(cells[i].rows || 0,ret);
15608 //        }
15609 //        return ret;
15610 //        
15611 //    },
15612     
15613     
15614     addItem : function(ev)
15615     {
15616         // look for vertical location slot in
15617         var cells = this.findCells(ev);
15618         
15619 //        ev.row = this.findBestRow(cells);
15620         
15621         // work out the location.
15622         
15623         var crow = false;
15624         var rows = [];
15625         for(var i =0; i < cells.length; i++) {
15626             
15627             cells[i].row = cells[0].row;
15628             
15629             if(i == 0){
15630                 cells[i].row = cells[i].row + 1;
15631             }
15632             
15633             if (!crow) {
15634                 crow = {
15635                     start : cells[i],
15636                     end :  cells[i]
15637                 };
15638                 continue;
15639             }
15640             if (crow.start.getY() == cells[i].getY()) {
15641                 // on same row.
15642                 crow.end = cells[i];
15643                 continue;
15644             }
15645             // different row.
15646             rows.push(crow);
15647             crow = {
15648                 start: cells[i],
15649                 end : cells[i]
15650             };
15651             
15652         }
15653         
15654         rows.push(crow);
15655         ev.els = [];
15656         ev.rows = rows;
15657         ev.cells = cells;
15658         
15659         cells[0].events.push(ev);
15660         
15661         this.calevents.push(ev);
15662     },
15663     
15664     clearEvents: function() {
15665         
15666         if(!this.calevents){
15667             return;
15668         }
15669         
15670         Roo.each(this.cells.elements, function(c){
15671             c.row = 0;
15672             c.events = [];
15673             c.more = [];
15674         });
15675         
15676         Roo.each(this.calevents, function(e) {
15677             Roo.each(e.els, function(el) {
15678                 el.un('mouseenter' ,this.onEventEnter, this);
15679                 el.un('mouseleave' ,this.onEventLeave, this);
15680                 el.remove();
15681             },this);
15682         },this);
15683         
15684         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15685             e.remove();
15686         });
15687         
15688     },
15689     
15690     renderEvents: function()
15691     {   
15692         var _this = this;
15693         
15694         this.cells.each(function(c) {
15695             
15696             if(c.row < 5){
15697                 return;
15698             }
15699             
15700             var ev = c.events;
15701             
15702             var r = 4;
15703             if(c.row != c.events.length){
15704                 r = 4 - (4 - (c.row - c.events.length));
15705             }
15706             
15707             c.events = ev.slice(0, r);
15708             c.more = ev.slice(r);
15709             
15710             if(c.more.length && c.more.length == 1){
15711                 c.events.push(c.more.pop());
15712             }
15713             
15714             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15715             
15716         });
15717             
15718         this.cells.each(function(c) {
15719             
15720             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15721             
15722             
15723             for (var e = 0; e < c.events.length; e++){
15724                 var ev = c.events[e];
15725                 var rows = ev.rows;
15726                 
15727                 for(var i = 0; i < rows.length; i++) {
15728                 
15729                     // how many rows should it span..
15730
15731                     var  cfg = {
15732                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15733                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15734
15735                         unselectable : "on",
15736                         cn : [
15737                             {
15738                                 cls: 'fc-event-inner',
15739                                 cn : [
15740     //                                {
15741     //                                  tag:'span',
15742     //                                  cls: 'fc-event-time',
15743     //                                  html : cells.length > 1 ? '' : ev.time
15744     //                                },
15745                                     {
15746                                       tag:'span',
15747                                       cls: 'fc-event-title',
15748                                       html : String.format('{0}', ev.title)
15749                                     }
15750
15751
15752                                 ]
15753                             },
15754                             {
15755                                 cls: 'ui-resizable-handle ui-resizable-e',
15756                                 html : '&nbsp;&nbsp;&nbsp'
15757                             }
15758
15759                         ]
15760                     };
15761
15762                     if (i == 0) {
15763                         cfg.cls += ' fc-event-start';
15764                     }
15765                     if ((i+1) == rows.length) {
15766                         cfg.cls += ' fc-event-end';
15767                     }
15768
15769                     var ctr = _this.el.select('.fc-event-container',true).first();
15770                     var cg = ctr.createChild(cfg);
15771
15772                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15773                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15774
15775                     var r = (c.more.length) ? 1 : 0;
15776                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15777                     cg.setWidth(ebox.right - sbox.x -2);
15778
15779                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15780                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15781                     cg.on('click', _this.onEventClick, _this, ev);
15782
15783                     ev.els.push(cg);
15784                     
15785                 }
15786                 
15787             }
15788             
15789             
15790             if(c.more.length){
15791                 var  cfg = {
15792                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15793                     style : 'position: absolute',
15794                     unselectable : "on",
15795                     cn : [
15796                         {
15797                             cls: 'fc-event-inner',
15798                             cn : [
15799                                 {
15800                                   tag:'span',
15801                                   cls: 'fc-event-title',
15802                                   html : 'More'
15803                                 }
15804
15805
15806                             ]
15807                         },
15808                         {
15809                             cls: 'ui-resizable-handle ui-resizable-e',
15810                             html : '&nbsp;&nbsp;&nbsp'
15811                         }
15812
15813                     ]
15814                 };
15815
15816                 var ctr = _this.el.select('.fc-event-container',true).first();
15817                 var cg = ctr.createChild(cfg);
15818
15819                 var sbox = c.select('.fc-day-content',true).first().getBox();
15820                 var ebox = c.select('.fc-day-content',true).first().getBox();
15821                 //Roo.log(cg);
15822                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15823                 cg.setWidth(ebox.right - sbox.x -2);
15824
15825                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15826                 
15827             }
15828             
15829         });
15830         
15831         
15832         
15833     },
15834     
15835     onEventEnter: function (e, el,event,d) {
15836         this.fireEvent('evententer', this, el, event);
15837     },
15838     
15839     onEventLeave: function (e, el,event,d) {
15840         this.fireEvent('eventleave', this, el, event);
15841     },
15842     
15843     onEventClick: function (e, el,event,d) {
15844         this.fireEvent('eventclick', this, el, event);
15845     },
15846     
15847     onMonthChange: function () {
15848         this.store.load();
15849     },
15850     
15851     onMoreEventClick: function(e, el, more)
15852     {
15853         var _this = this;
15854         
15855         this.calpopover.placement = 'right';
15856         this.calpopover.setTitle('More');
15857         
15858         this.calpopover.setContent('');
15859         
15860         var ctr = this.calpopover.el.select('.popover-content', true).first();
15861         
15862         Roo.each(more, function(m){
15863             var cfg = {
15864                 cls : 'fc-event-hori fc-event-draggable',
15865                 html : m.title
15866             };
15867             var cg = ctr.createChild(cfg);
15868             
15869             cg.on('click', _this.onEventClick, _this, m);
15870         });
15871         
15872         this.calpopover.show(el);
15873         
15874         
15875     },
15876     
15877     onLoad: function () 
15878     {   
15879         this.calevents = [];
15880         var cal = this;
15881         
15882         if(this.store.getCount() > 0){
15883             this.store.data.each(function(d){
15884                cal.addItem({
15885                     id : d.data.id,
15886                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15887                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15888                     time : d.data.start_time,
15889                     title : d.data.title,
15890                     description : d.data.description,
15891                     venue : d.data.venue
15892                 });
15893             });
15894         }
15895         
15896         this.renderEvents();
15897         
15898         if(this.calevents.length && this.loadMask){
15899             this.maskEl.hide();
15900         }
15901     },
15902     
15903     onBeforeLoad: function()
15904     {
15905         this.clearEvents();
15906         if(this.loadMask){
15907             this.maskEl.show();
15908         }
15909     }
15910 });
15911
15912  
15913  /*
15914  * - LGPL
15915  *
15916  * element
15917  * 
15918  */
15919
15920 /**
15921  * @class Roo.bootstrap.Popover
15922  * @extends Roo.bootstrap.Component
15923  * Bootstrap Popover class
15924  * @cfg {String} html contents of the popover   (or false to use children..)
15925  * @cfg {String} title of popover (or false to hide)
15926  * @cfg {String} placement how it is placed
15927  * @cfg {String} trigger click || hover (or false to trigger manually)
15928  * @cfg {String} over what (parent or false to trigger manually.)
15929  * @cfg {Number} delay - delay before showing
15930  
15931  * @constructor
15932  * Create a new Popover
15933  * @param {Object} config The config object
15934  */
15935
15936 Roo.bootstrap.Popover = function(config){
15937     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15938     
15939     this.addEvents({
15940         // raw events
15941          /**
15942          * @event show
15943          * After the popover show
15944          * 
15945          * @param {Roo.bootstrap.Popover} this
15946          */
15947         "show" : true,
15948         /**
15949          * @event hide
15950          * After the popover hide
15951          * 
15952          * @param {Roo.bootstrap.Popover} this
15953          */
15954         "hide" : true
15955     });
15956 };
15957
15958 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15959     
15960     title: 'Fill in a title',
15961     html: false,
15962     
15963     placement : 'right',
15964     trigger : 'hover', // hover
15965     
15966     delay : 0,
15967     
15968     over: 'parent',
15969     
15970     can_build_overlaid : false,
15971     
15972     getChildContainer : function()
15973     {
15974         return this.el.select('.popover-content',true).first();
15975     },
15976     
15977     getAutoCreate : function(){
15978          
15979         var cfg = {
15980            cls : 'popover roo-dynamic',
15981            style: 'display:block',
15982            cn : [
15983                 {
15984                     cls : 'arrow'
15985                 },
15986                 {
15987                     cls : 'popover-inner',
15988                     cn : [
15989                         {
15990                             tag: 'h3',
15991                             cls: 'popover-title',
15992                             html : this.title
15993                         },
15994                         {
15995                             cls : 'popover-content',
15996                             html : this.html
15997                         }
15998                     ]
15999                     
16000                 }
16001            ]
16002         };
16003         
16004         return cfg;
16005     },
16006     setTitle: function(str)
16007     {
16008         this.title = str;
16009         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16010     },
16011     setContent: function(str)
16012     {
16013         this.html = str;
16014         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16015     },
16016     // as it get's added to the bottom of the page.
16017     onRender : function(ct, position)
16018     {
16019         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16020         if(!this.el){
16021             var cfg = Roo.apply({},  this.getAutoCreate());
16022             cfg.id = Roo.id();
16023             
16024             if (this.cls) {
16025                 cfg.cls += ' ' + this.cls;
16026             }
16027             if (this.style) {
16028                 cfg.style = this.style;
16029             }
16030             //Roo.log("adding to ");
16031             this.el = Roo.get(document.body).createChild(cfg, position);
16032 //            Roo.log(this.el);
16033         }
16034         this.initEvents();
16035     },
16036     
16037     initEvents : function()
16038     {
16039         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16040         this.el.enableDisplayMode('block');
16041         this.el.hide();
16042         if (this.over === false) {
16043             return; 
16044         }
16045         if (this.triggers === false) {
16046             return;
16047         }
16048         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16049         var triggers = this.trigger ? this.trigger.split(' ') : [];
16050         Roo.each(triggers, function(trigger) {
16051         
16052             if (trigger == 'click') {
16053                 on_el.on('click', this.toggle, this);
16054             } else if (trigger != 'manual') {
16055                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16056                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16057       
16058                 on_el.on(eventIn  ,this.enter, this);
16059                 on_el.on(eventOut, this.leave, this);
16060             }
16061         }, this);
16062         
16063     },
16064     
16065     
16066     // private
16067     timeout : null,
16068     hoverState : null,
16069     
16070     toggle : function () {
16071         this.hoverState == 'in' ? this.leave() : this.enter();
16072     },
16073     
16074     enter : function () {
16075         
16076         clearTimeout(this.timeout);
16077     
16078         this.hoverState = 'in';
16079     
16080         if (!this.delay || !this.delay.show) {
16081             this.show();
16082             return;
16083         }
16084         var _t = this;
16085         this.timeout = setTimeout(function () {
16086             if (_t.hoverState == 'in') {
16087                 _t.show();
16088             }
16089         }, this.delay.show)
16090     },
16091     
16092     leave : function() {
16093         clearTimeout(this.timeout);
16094     
16095         this.hoverState = 'out';
16096     
16097         if (!this.delay || !this.delay.hide) {
16098             this.hide();
16099             return;
16100         }
16101         var _t = this;
16102         this.timeout = setTimeout(function () {
16103             if (_t.hoverState == 'out') {
16104                 _t.hide();
16105             }
16106         }, this.delay.hide)
16107     },
16108     
16109     show : function (on_el)
16110     {
16111         if (!on_el) {
16112             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16113         }
16114         
16115         // set content.
16116         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16117         if (this.html !== false) {
16118             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16119         }
16120         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16121         if (!this.title.length) {
16122             this.el.select('.popover-title',true).hide();
16123         }
16124         
16125         var placement = typeof this.placement == 'function' ?
16126             this.placement.call(this, this.el, on_el) :
16127             this.placement;
16128             
16129         var autoToken = /\s?auto?\s?/i;
16130         var autoPlace = autoToken.test(placement);
16131         if (autoPlace) {
16132             placement = placement.replace(autoToken, '') || 'top';
16133         }
16134         
16135         //this.el.detach()
16136         //this.el.setXY([0,0]);
16137         this.el.show();
16138         this.el.dom.style.display='block';
16139         this.el.addClass(placement);
16140         
16141         //this.el.appendTo(on_el);
16142         
16143         var p = this.getPosition();
16144         var box = this.el.getBox();
16145         
16146         if (autoPlace) {
16147             // fixme..
16148         }
16149         var align = Roo.bootstrap.Popover.alignment[placement];
16150         this.el.alignTo(on_el, align[0],align[1]);
16151         //var arrow = this.el.select('.arrow',true).first();
16152         //arrow.set(align[2], 
16153         
16154         this.el.addClass('in');
16155         
16156         
16157         if (this.el.hasClass('fade')) {
16158             // fade it?
16159         }
16160         
16161         this.hoverState = 'in';
16162         
16163         this.fireEvent('show', this);
16164         
16165     },
16166     hide : function()
16167     {
16168         this.el.setXY([0,0]);
16169         this.el.removeClass('in');
16170         this.el.hide();
16171         this.hoverState = null;
16172         
16173         this.fireEvent('hide', this);
16174     }
16175     
16176 });
16177
16178 Roo.bootstrap.Popover.alignment = {
16179     'left' : ['r-l', [-10,0], 'right'],
16180     'right' : ['l-r', [10,0], 'left'],
16181     'bottom' : ['t-b', [0,10], 'top'],
16182     'top' : [ 'b-t', [0,-10], 'bottom']
16183 };
16184
16185  /*
16186  * - LGPL
16187  *
16188  * Progress
16189  * 
16190  */
16191
16192 /**
16193  * @class Roo.bootstrap.Progress
16194  * @extends Roo.bootstrap.Component
16195  * Bootstrap Progress class
16196  * @cfg {Boolean} striped striped of the progress bar
16197  * @cfg {Boolean} active animated of the progress bar
16198  * 
16199  * 
16200  * @constructor
16201  * Create a new Progress
16202  * @param {Object} config The config object
16203  */
16204
16205 Roo.bootstrap.Progress = function(config){
16206     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16207 };
16208
16209 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16210     
16211     striped : false,
16212     active: false,
16213     
16214     getAutoCreate : function(){
16215         var cfg = {
16216             tag: 'div',
16217             cls: 'progress'
16218         };
16219         
16220         
16221         if(this.striped){
16222             cfg.cls += ' progress-striped';
16223         }
16224       
16225         if(this.active){
16226             cfg.cls += ' active';
16227         }
16228         
16229         
16230         return cfg;
16231     }
16232    
16233 });
16234
16235  
16236
16237  /*
16238  * - LGPL
16239  *
16240  * ProgressBar
16241  * 
16242  */
16243
16244 /**
16245  * @class Roo.bootstrap.ProgressBar
16246  * @extends Roo.bootstrap.Component
16247  * Bootstrap ProgressBar class
16248  * @cfg {Number} aria_valuenow aria-value now
16249  * @cfg {Number} aria_valuemin aria-value min
16250  * @cfg {Number} aria_valuemax aria-value max
16251  * @cfg {String} label label for the progress bar
16252  * @cfg {String} panel (success | info | warning | danger )
16253  * @cfg {String} role role of the progress bar
16254  * @cfg {String} sr_only text
16255  * 
16256  * 
16257  * @constructor
16258  * Create a new ProgressBar
16259  * @param {Object} config The config object
16260  */
16261
16262 Roo.bootstrap.ProgressBar = function(config){
16263     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16264 };
16265
16266 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16267     
16268     aria_valuenow : 0,
16269     aria_valuemin : 0,
16270     aria_valuemax : 100,
16271     label : false,
16272     panel : false,
16273     role : false,
16274     sr_only: false,
16275     
16276     getAutoCreate : function()
16277     {
16278         
16279         var cfg = {
16280             tag: 'div',
16281             cls: 'progress-bar',
16282             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16283         };
16284         
16285         if(this.sr_only){
16286             cfg.cn = {
16287                 tag: 'span',
16288                 cls: 'sr-only',
16289                 html: this.sr_only
16290             }
16291         }
16292         
16293         if(this.role){
16294             cfg.role = this.role;
16295         }
16296         
16297         if(this.aria_valuenow){
16298             cfg['aria-valuenow'] = this.aria_valuenow;
16299         }
16300         
16301         if(this.aria_valuemin){
16302             cfg['aria-valuemin'] = this.aria_valuemin;
16303         }
16304         
16305         if(this.aria_valuemax){
16306             cfg['aria-valuemax'] = this.aria_valuemax;
16307         }
16308         
16309         if(this.label && !this.sr_only){
16310             cfg.html = this.label;
16311         }
16312         
16313         if(this.panel){
16314             cfg.cls += ' progress-bar-' + this.panel;
16315         }
16316         
16317         return cfg;
16318     },
16319     
16320     update : function(aria_valuenow)
16321     {
16322         this.aria_valuenow = aria_valuenow;
16323         
16324         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16325     }
16326    
16327 });
16328
16329  
16330
16331  /*
16332  * - LGPL
16333  *
16334  * column
16335  * 
16336  */
16337
16338 /**
16339  * @class Roo.bootstrap.TabGroup
16340  * @extends Roo.bootstrap.Column
16341  * Bootstrap Column class
16342  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16343  * @cfg {Boolean} carousel true to make the group behave like a carousel
16344  * @cfg {Boolean} bullets show bullets for the panels
16345  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16346  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16347  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16348  * 
16349  * @constructor
16350  * Create a new TabGroup
16351  * @param {Object} config The config object
16352  */
16353
16354 Roo.bootstrap.TabGroup = function(config){
16355     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16356     if (!this.navId) {
16357         this.navId = Roo.id();
16358     }
16359     this.tabs = [];
16360     Roo.bootstrap.TabGroup.register(this);
16361     
16362 };
16363
16364 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16365     
16366     carousel : false,
16367     transition : false,
16368     bullets : 0,
16369     timer : 0,
16370     autoslide : false,
16371     slideFn : false,
16372     slideOnTouch : false,
16373     
16374     getAutoCreate : function()
16375     {
16376         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16377         
16378         cfg.cls += ' tab-content';
16379         
16380         if (this.carousel) {
16381             cfg.cls += ' carousel slide';
16382             
16383             cfg.cn = [{
16384                cls : 'carousel-inner'
16385             }];
16386         
16387             if(this.bullets  && !Roo.isTouch){
16388                 
16389                 var bullets = {
16390                     cls : 'carousel-bullets',
16391                     cn : []
16392                 };
16393                
16394                 if(this.bullets_cls){
16395                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16396                 }
16397                  /*
16398                 for (var i = 0; i < this.bullets; i++){
16399                     bullets.cn.push({
16400                         cls : 'bullet bullet-' + i
16401                     });
16402                 }
16403                 */
16404                 bullets.cn.push({
16405                     cls : 'clear'
16406                 });
16407                 
16408                 cfg.cn[0].cn = bullets;
16409             }
16410         }
16411         
16412         return cfg;
16413     },
16414     
16415     initEvents:  function()
16416     {
16417         if(Roo.isTouch && this.slideOnTouch){
16418             this.el.on("touchstart", this.onTouchStart, this);
16419         }
16420         
16421         if(this.autoslide){
16422             var _this = this;
16423             
16424             this.slideFn = window.setInterval(function() {
16425                 _this.showPanelNext();
16426             }, this.timer);
16427         }
16428         
16429     },
16430     
16431     onTouchStart : function(e, el, o)
16432     {
16433         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16434             return;
16435         }
16436         
16437         this.showPanelNext();
16438     },
16439     
16440     getChildContainer : function()
16441     {
16442         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16443     },
16444     
16445     /**
16446     * register a Navigation item
16447     * @param {Roo.bootstrap.NavItem} the navitem to add
16448     */
16449     register : function(item)
16450     {
16451         this.tabs.push( item);
16452         item.navId = this.navId; // not really needed..
16453         this.addBullet();
16454     
16455     },
16456     
16457     getActivePanel : function()
16458     {
16459         var r = false;
16460         Roo.each(this.tabs, function(t) {
16461             if (t.active) {
16462                 r = t;
16463                 return false;
16464             }
16465             return null;
16466         });
16467         return r;
16468         
16469     },
16470     getPanelByName : function(n)
16471     {
16472         var r = false;
16473         Roo.each(this.tabs, function(t) {
16474             if (t.tabId == n) {
16475                 r = t;
16476                 return false;
16477             }
16478             return null;
16479         });
16480         return r;
16481     },
16482     indexOfPanel : function(p)
16483     {
16484         var r = false;
16485         Roo.each(this.tabs, function(t,i) {
16486             if (t.tabId == p.tabId) {
16487                 r = i;
16488                 return false;
16489             }
16490             return null;
16491         });
16492         return r;
16493     },
16494     /**
16495      * show a specific panel
16496      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16497      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16498      */
16499     showPanel : function (pan)
16500     {
16501         if(this.transition || typeof(pan) == 'undefined'){
16502             Roo.log("waiting for the transitionend");
16503             return;
16504         }
16505         
16506         if (typeof(pan) == 'number') {
16507             pan = this.tabs[pan];
16508         }
16509         
16510         if (typeof(pan) == 'string') {
16511             pan = this.getPanelByName(pan);
16512         }
16513         
16514         var cur = this.getActivePanel();
16515         
16516         if(!pan || !cur){
16517             Roo.log('pan or acitve pan is undefined');
16518             return false;
16519         }
16520         
16521         if (pan.tabId == this.getActivePanel().tabId) {
16522             return true;
16523         }
16524         
16525         if (false === cur.fireEvent('beforedeactivate')) {
16526             return false;
16527         }
16528         
16529         if(this.bullets > 0 && !Roo.isTouch){
16530             this.setActiveBullet(this.indexOfPanel(pan));
16531         }
16532         
16533         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16534             
16535             this.transition = true;
16536             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16537             var lr = dir == 'next' ? 'left' : 'right';
16538             pan.el.addClass(dir); // or prev
16539             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16540             cur.el.addClass(lr); // or right
16541             pan.el.addClass(lr);
16542             
16543             var _this = this;
16544             cur.el.on('transitionend', function() {
16545                 Roo.log("trans end?");
16546                 
16547                 pan.el.removeClass([lr,dir]);
16548                 pan.setActive(true);
16549                 
16550                 cur.el.removeClass([lr]);
16551                 cur.setActive(false);
16552                 
16553                 _this.transition = false;
16554                 
16555             }, this, { single:  true } );
16556             
16557             return true;
16558         }
16559         
16560         cur.setActive(false);
16561         pan.setActive(true);
16562         
16563         return true;
16564         
16565     },
16566     showPanelNext : function()
16567     {
16568         var i = this.indexOfPanel(this.getActivePanel());
16569         
16570         if (i >= this.tabs.length - 1 && !this.autoslide) {
16571             return;
16572         }
16573         
16574         if (i >= this.tabs.length - 1 && this.autoslide) {
16575             i = -1;
16576         }
16577         
16578         this.showPanel(this.tabs[i+1]);
16579     },
16580     
16581     showPanelPrev : function()
16582     {
16583         var i = this.indexOfPanel(this.getActivePanel());
16584         
16585         if (i  < 1 && !this.autoslide) {
16586             return;
16587         }
16588         
16589         if (i < 1 && this.autoslide) {
16590             i = this.tabs.length;
16591         }
16592         
16593         this.showPanel(this.tabs[i-1]);
16594     },
16595     
16596     
16597     addBullet: function()
16598     {
16599         if(!this.bullets || Roo.isTouch){
16600             return;
16601         }
16602         var ctr = this.el.select('.carousel-bullets',true).first();
16603         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16604         var bullet = ctr.createChild({
16605             cls : 'bullet bullet-' + i
16606         },ctr.dom.lastChild);
16607         
16608         
16609         var _this = this;
16610         
16611         bullet.on('click', (function(e, el, o, ii, t){
16612
16613             e.preventDefault();
16614
16615             this.showPanel(ii);
16616
16617             if(this.autoslide && this.slideFn){
16618                 clearInterval(this.slideFn);
16619                 this.slideFn = window.setInterval(function() {
16620                     _this.showPanelNext();
16621                 }, this.timer);
16622             }
16623
16624         }).createDelegate(this, [i, bullet], true));
16625                 
16626         
16627     },
16628      
16629     setActiveBullet : function(i)
16630     {
16631         if(Roo.isTouch){
16632             return;
16633         }
16634         
16635         Roo.each(this.el.select('.bullet', true).elements, function(el){
16636             el.removeClass('selected');
16637         });
16638
16639         var bullet = this.el.select('.bullet-' + i, true).first();
16640         
16641         if(!bullet){
16642             return;
16643         }
16644         
16645         bullet.addClass('selected');
16646     }
16647     
16648     
16649   
16650 });
16651
16652  
16653
16654  
16655  
16656 Roo.apply(Roo.bootstrap.TabGroup, {
16657     
16658     groups: {},
16659      /**
16660     * register a Navigation Group
16661     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16662     */
16663     register : function(navgrp)
16664     {
16665         this.groups[navgrp.navId] = navgrp;
16666         
16667     },
16668     /**
16669     * fetch a Navigation Group based on the navigation ID
16670     * if one does not exist , it will get created.
16671     * @param {string} the navgroup to add
16672     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16673     */
16674     get: function(navId) {
16675         if (typeof(this.groups[navId]) == 'undefined') {
16676             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16677         }
16678         return this.groups[navId] ;
16679     }
16680     
16681     
16682     
16683 });
16684
16685  /*
16686  * - LGPL
16687  *
16688  * TabPanel
16689  * 
16690  */
16691
16692 /**
16693  * @class Roo.bootstrap.TabPanel
16694  * @extends Roo.bootstrap.Component
16695  * Bootstrap TabPanel class
16696  * @cfg {Boolean} active panel active
16697  * @cfg {String} html panel content
16698  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16699  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16700  * 
16701  * 
16702  * @constructor
16703  * Create a new TabPanel
16704  * @param {Object} config The config object
16705  */
16706
16707 Roo.bootstrap.TabPanel = function(config){
16708     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16709     this.addEvents({
16710         /**
16711              * @event changed
16712              * Fires when the active status changes
16713              * @param {Roo.bootstrap.TabPanel} this
16714              * @param {Boolean} state the new state
16715             
16716          */
16717         'changed': true,
16718         /**
16719              * @event beforedeactivate
16720              * Fires before a tab is de-activated - can be used to do validation on a form.
16721              * @param {Roo.bootstrap.TabPanel} this
16722              * @return {Boolean} false if there is an error
16723             
16724          */
16725         'beforedeactivate': true
16726      });
16727     
16728     this.tabId = this.tabId || Roo.id();
16729   
16730 };
16731
16732 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16733     
16734     active: false,
16735     html: false,
16736     tabId: false,
16737     navId : false,
16738     
16739     getAutoCreate : function(){
16740         var cfg = {
16741             tag: 'div',
16742             // item is needed for carousel - not sure if it has any effect otherwise
16743             cls: 'tab-pane item',
16744             html: this.html || ''
16745         };
16746         
16747         if(this.active){
16748             cfg.cls += ' active';
16749         }
16750         
16751         if(this.tabId){
16752             cfg.tabId = this.tabId;
16753         }
16754         
16755         
16756         return cfg;
16757     },
16758     
16759     initEvents:  function()
16760     {
16761         var p = this.parent();
16762         this.navId = this.navId || p.navId;
16763         
16764         if (typeof(this.navId) != 'undefined') {
16765             // not really needed.. but just in case.. parent should be a NavGroup.
16766             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16767             
16768             tg.register(this);
16769             
16770             var i = tg.tabs.length - 1;
16771             
16772             if(this.active && tg.bullets > 0 && i < tg.bullets){
16773                 tg.setActiveBullet(i);
16774             }
16775         }
16776         
16777     },
16778     
16779     
16780     onRender : function(ct, position)
16781     {
16782        // Roo.log("Call onRender: " + this.xtype);
16783         
16784         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16785         
16786         
16787         
16788         
16789         
16790     },
16791     
16792     setActive: function(state)
16793     {
16794         Roo.log("panel - set active " + this.tabId + "=" + state);
16795         
16796         this.active = state;
16797         if (!state) {
16798             this.el.removeClass('active');
16799             
16800         } else  if (!this.el.hasClass('active')) {
16801             this.el.addClass('active');
16802         }
16803         
16804         this.fireEvent('changed', this, state);
16805     }
16806     
16807     
16808 });
16809  
16810
16811  
16812
16813  /*
16814  * - LGPL
16815  *
16816  * DateField
16817  * 
16818  */
16819
16820 /**
16821  * @class Roo.bootstrap.DateField
16822  * @extends Roo.bootstrap.Input
16823  * Bootstrap DateField class
16824  * @cfg {Number} weekStart default 0
16825  * @cfg {String} viewMode default empty, (months|years)
16826  * @cfg {String} minViewMode default empty, (months|years)
16827  * @cfg {Number} startDate default -Infinity
16828  * @cfg {Number} endDate default Infinity
16829  * @cfg {Boolean} todayHighlight default false
16830  * @cfg {Boolean} todayBtn default false
16831  * @cfg {Boolean} calendarWeeks default false
16832  * @cfg {Object} daysOfWeekDisabled default empty
16833  * @cfg {Boolean} singleMode default false (true | false)
16834  * 
16835  * @cfg {Boolean} keyboardNavigation default true
16836  * @cfg {String} language default en
16837  * 
16838  * @constructor
16839  * Create a new DateField
16840  * @param {Object} config The config object
16841  */
16842
16843 Roo.bootstrap.DateField = function(config){
16844     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16845      this.addEvents({
16846             /**
16847              * @event show
16848              * Fires when this field show.
16849              * @param {Roo.bootstrap.DateField} this
16850              * @param {Mixed} date The date value
16851              */
16852             show : true,
16853             /**
16854              * @event show
16855              * Fires when this field hide.
16856              * @param {Roo.bootstrap.DateField} this
16857              * @param {Mixed} date The date value
16858              */
16859             hide : true,
16860             /**
16861              * @event select
16862              * Fires when select a date.
16863              * @param {Roo.bootstrap.DateField} this
16864              * @param {Mixed} date The date value
16865              */
16866             select : true,
16867             /**
16868              * @event beforeselect
16869              * Fires when before select a date.
16870              * @param {Roo.bootstrap.DateField} this
16871              * @param {Mixed} date The date value
16872              */
16873             beforeselect : true
16874         });
16875 };
16876
16877 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16878     
16879     /**
16880      * @cfg {String} format
16881      * The default date format string which can be overriden for localization support.  The format must be
16882      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16883      */
16884     format : "m/d/y",
16885     /**
16886      * @cfg {String} altFormats
16887      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16888      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16889      */
16890     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16891     
16892     weekStart : 0,
16893     
16894     viewMode : '',
16895     
16896     minViewMode : '',
16897     
16898     todayHighlight : false,
16899     
16900     todayBtn: false,
16901     
16902     language: 'en',
16903     
16904     keyboardNavigation: true,
16905     
16906     calendarWeeks: false,
16907     
16908     startDate: -Infinity,
16909     
16910     endDate: Infinity,
16911     
16912     daysOfWeekDisabled: [],
16913     
16914     _events: [],
16915     
16916     singleMode : false,
16917     
16918     UTCDate: function()
16919     {
16920         return new Date(Date.UTC.apply(Date, arguments));
16921     },
16922     
16923     UTCToday: function()
16924     {
16925         var today = new Date();
16926         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16927     },
16928     
16929     getDate: function() {
16930             var d = this.getUTCDate();
16931             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16932     },
16933     
16934     getUTCDate: function() {
16935             return this.date;
16936     },
16937     
16938     setDate: function(d) {
16939             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16940     },
16941     
16942     setUTCDate: function(d) {
16943             this.date = d;
16944             this.setValue(this.formatDate(this.date));
16945     },
16946         
16947     onRender: function(ct, position)
16948     {
16949         
16950         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16951         
16952         this.language = this.language || 'en';
16953         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16954         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16955         
16956         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16957         this.format = this.format || 'm/d/y';
16958         this.isInline = false;
16959         this.isInput = true;
16960         this.component = this.el.select('.add-on', true).first() || false;
16961         this.component = (this.component && this.component.length === 0) ? false : this.component;
16962         this.hasInput = this.component && this.inputEL().length;
16963         
16964         if (typeof(this.minViewMode === 'string')) {
16965             switch (this.minViewMode) {
16966                 case 'months':
16967                     this.minViewMode = 1;
16968                     break;
16969                 case 'years':
16970                     this.minViewMode = 2;
16971                     break;
16972                 default:
16973                     this.minViewMode = 0;
16974                     break;
16975             }
16976         }
16977         
16978         if (typeof(this.viewMode === 'string')) {
16979             switch (this.viewMode) {
16980                 case 'months':
16981                     this.viewMode = 1;
16982                     break;
16983                 case 'years':
16984                     this.viewMode = 2;
16985                     break;
16986                 default:
16987                     this.viewMode = 0;
16988                     break;
16989             }
16990         }
16991                 
16992         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16993         
16994 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16995         
16996         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16997         
16998         this.picker().on('mousedown', this.onMousedown, this);
16999         this.picker().on('click', this.onClick, this);
17000         
17001         this.picker().addClass('datepicker-dropdown');
17002         
17003         this.startViewMode = this.viewMode;
17004         
17005         if(this.singleMode){
17006             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17007                 v.setVisibilityMode(Roo.Element.DISPLAY);
17008                 v.hide();
17009             });
17010             
17011             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17012                 v.setStyle('width', '189px');
17013             });
17014         }
17015         
17016         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17017             if(!this.calendarWeeks){
17018                 v.remove();
17019                 return;
17020             }
17021             
17022             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17023             v.attr('colspan', function(i, val){
17024                 return parseInt(val) + 1;
17025             });
17026         });
17027                         
17028         
17029         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17030         
17031         this.setStartDate(this.startDate);
17032         this.setEndDate(this.endDate);
17033         
17034         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17035         
17036         this.fillDow();
17037         this.fillMonths();
17038         this.update();
17039         this.showMode();
17040         
17041         if(this.isInline) {
17042             this.show();
17043         }
17044     },
17045     
17046     picker : function()
17047     {
17048         return this.pickerEl;
17049 //        return this.el.select('.datepicker', true).first();
17050     },
17051     
17052     fillDow: function()
17053     {
17054         var dowCnt = this.weekStart;
17055         
17056         var dow = {
17057             tag: 'tr',
17058             cn: [
17059                 
17060             ]
17061         };
17062         
17063         if(this.calendarWeeks){
17064             dow.cn.push({
17065                 tag: 'th',
17066                 cls: 'cw',
17067                 html: '&nbsp;'
17068             })
17069         }
17070         
17071         while (dowCnt < this.weekStart + 7) {
17072             dow.cn.push({
17073                 tag: 'th',
17074                 cls: 'dow',
17075                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17076             });
17077         }
17078         
17079         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17080     },
17081     
17082     fillMonths: function()
17083     {    
17084         var i = 0;
17085         var months = this.picker().select('>.datepicker-months td', true).first();
17086         
17087         months.dom.innerHTML = '';
17088         
17089         while (i < 12) {
17090             var month = {
17091                 tag: 'span',
17092                 cls: 'month',
17093                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17094             };
17095             
17096             months.createChild(month);
17097         }
17098         
17099     },
17100     
17101     update: function()
17102     {
17103         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;
17104         
17105         if (this.date < this.startDate) {
17106             this.viewDate = new Date(this.startDate);
17107         } else if (this.date > this.endDate) {
17108             this.viewDate = new Date(this.endDate);
17109         } else {
17110             this.viewDate = new Date(this.date);
17111         }
17112         
17113         this.fill();
17114     },
17115     
17116     fill: function() 
17117     {
17118         var d = new Date(this.viewDate),
17119                 year = d.getUTCFullYear(),
17120                 month = d.getUTCMonth(),
17121                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17122                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17123                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17124                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17125                 currentDate = this.date && this.date.valueOf(),
17126                 today = this.UTCToday();
17127         
17128         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17129         
17130 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17131         
17132 //        this.picker.select('>tfoot th.today').
17133 //                                              .text(dates[this.language].today)
17134 //                                              .toggle(this.todayBtn !== false);
17135     
17136         this.updateNavArrows();
17137         this.fillMonths();
17138                                                 
17139         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17140         
17141         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17142          
17143         prevMonth.setUTCDate(day);
17144         
17145         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17146         
17147         var nextMonth = new Date(prevMonth);
17148         
17149         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17150         
17151         nextMonth = nextMonth.valueOf();
17152         
17153         var fillMonths = false;
17154         
17155         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17156         
17157         while(prevMonth.valueOf() < nextMonth) {
17158             var clsName = '';
17159             
17160             if (prevMonth.getUTCDay() === this.weekStart) {
17161                 if(fillMonths){
17162                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17163                 }
17164                     
17165                 fillMonths = {
17166                     tag: 'tr',
17167                     cn: []
17168                 };
17169                 
17170                 if(this.calendarWeeks){
17171                     // ISO 8601: First week contains first thursday.
17172                     // ISO also states week starts on Monday, but we can be more abstract here.
17173                     var
17174                     // Start of current week: based on weekstart/current date
17175                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17176                     // Thursday of this week
17177                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17178                     // First Thursday of year, year from thursday
17179                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17180                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17181                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17182                     
17183                     fillMonths.cn.push({
17184                         tag: 'td',
17185                         cls: 'cw',
17186                         html: calWeek
17187                     });
17188                 }
17189             }
17190             
17191             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17192                 clsName += ' old';
17193             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17194                 clsName += ' new';
17195             }
17196             if (this.todayHighlight &&
17197                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17198                 prevMonth.getUTCMonth() == today.getMonth() &&
17199                 prevMonth.getUTCDate() == today.getDate()) {
17200                 clsName += ' today';
17201             }
17202             
17203             if (currentDate && prevMonth.valueOf() === currentDate) {
17204                 clsName += ' active';
17205             }
17206             
17207             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17208                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17209                     clsName += ' disabled';
17210             }
17211             
17212             fillMonths.cn.push({
17213                 tag: 'td',
17214                 cls: 'day ' + clsName,
17215                 html: prevMonth.getDate()
17216             });
17217             
17218             prevMonth.setDate(prevMonth.getDate()+1);
17219         }
17220           
17221         var currentYear = this.date && this.date.getUTCFullYear();
17222         var currentMonth = this.date && this.date.getUTCMonth();
17223         
17224         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17225         
17226         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17227             v.removeClass('active');
17228             
17229             if(currentYear === year && k === currentMonth){
17230                 v.addClass('active');
17231             }
17232             
17233             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17234                 v.addClass('disabled');
17235             }
17236             
17237         });
17238         
17239         
17240         year = parseInt(year/10, 10) * 10;
17241         
17242         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17243         
17244         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17245         
17246         year -= 1;
17247         for (var i = -1; i < 11; i++) {
17248             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17249                 tag: 'span',
17250                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17251                 html: year
17252             });
17253             
17254             year += 1;
17255         }
17256     },
17257     
17258     showMode: function(dir) 
17259     {
17260         if (dir) {
17261             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17262         }
17263         
17264         Roo.each(this.picker().select('>div',true).elements, function(v){
17265             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17266             v.hide();
17267         });
17268         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17269     },
17270     
17271     place: function()
17272     {
17273         if(this.isInline) {
17274             return;
17275         }
17276         
17277         this.picker().removeClass(['bottom', 'top']);
17278         
17279         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17280             /*
17281              * place to the top of element!
17282              *
17283              */
17284             
17285             this.picker().addClass('top');
17286             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17287             
17288             return;
17289         }
17290         
17291         this.picker().addClass('bottom');
17292         
17293         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17294     },
17295     
17296     parseDate : function(value)
17297     {
17298         if(!value || value instanceof Date){
17299             return value;
17300         }
17301         var v = Date.parseDate(value, this.format);
17302         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17303             v = Date.parseDate(value, 'Y-m-d');
17304         }
17305         if(!v && this.altFormats){
17306             if(!this.altFormatsArray){
17307                 this.altFormatsArray = this.altFormats.split("|");
17308             }
17309             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17310                 v = Date.parseDate(value, this.altFormatsArray[i]);
17311             }
17312         }
17313         return v;
17314     },
17315     
17316     formatDate : function(date, fmt)
17317     {   
17318         return (!date || !(date instanceof Date)) ?
17319         date : date.dateFormat(fmt || this.format);
17320     },
17321     
17322     onFocus : function()
17323     {
17324         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17325         this.show();
17326     },
17327     
17328     onBlur : function()
17329     {
17330         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17331         
17332         var d = this.inputEl().getValue();
17333         
17334         this.setValue(d);
17335                 
17336         this.hide();
17337     },
17338     
17339     show : function()
17340     {
17341         this.picker().show();
17342         this.update();
17343         this.place();
17344         
17345         this.fireEvent('show', this, this.date);
17346     },
17347     
17348     hide : function()
17349     {
17350         if(this.isInline) {
17351             return;
17352         }
17353         this.picker().hide();
17354         this.viewMode = this.startViewMode;
17355         this.showMode();
17356         
17357         this.fireEvent('hide', this, this.date);
17358         
17359     },
17360     
17361     onMousedown: function(e)
17362     {
17363         e.stopPropagation();
17364         e.preventDefault();
17365     },
17366     
17367     keyup: function(e)
17368     {
17369         Roo.bootstrap.DateField.superclass.keyup.call(this);
17370         this.update();
17371     },
17372
17373     setValue: function(v)
17374     {
17375         if(this.fireEvent('beforeselect', this, v) !== false){
17376             var d = new Date(this.parseDate(v) ).clearTime();
17377         
17378             if(isNaN(d.getTime())){
17379                 this.date = this.viewDate = '';
17380                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17381                 return;
17382             }
17383
17384             v = this.formatDate(d);
17385
17386             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17387
17388             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17389
17390             this.update();
17391
17392             this.fireEvent('select', this, this.date);
17393         }
17394     },
17395     
17396     getValue: function()
17397     {
17398         return this.formatDate(this.date);
17399     },
17400     
17401     fireKey: function(e)
17402     {
17403         if (!this.picker().isVisible()){
17404             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17405                 this.show();
17406             }
17407             return;
17408         }
17409         
17410         var dateChanged = false,
17411         dir, day, month,
17412         newDate, newViewDate;
17413         
17414         switch(e.keyCode){
17415             case 27: // escape
17416                 this.hide();
17417                 e.preventDefault();
17418                 break;
17419             case 37: // left
17420             case 39: // right
17421                 if (!this.keyboardNavigation) {
17422                     break;
17423                 }
17424                 dir = e.keyCode == 37 ? -1 : 1;
17425                 
17426                 if (e.ctrlKey){
17427                     newDate = this.moveYear(this.date, dir);
17428                     newViewDate = this.moveYear(this.viewDate, dir);
17429                 } else if (e.shiftKey){
17430                     newDate = this.moveMonth(this.date, dir);
17431                     newViewDate = this.moveMonth(this.viewDate, dir);
17432                 } else {
17433                     newDate = new Date(this.date);
17434                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17435                     newViewDate = new Date(this.viewDate);
17436                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17437                 }
17438                 if (this.dateWithinRange(newDate)){
17439                     this.date = newDate;
17440                     this.viewDate = newViewDate;
17441                     this.setValue(this.formatDate(this.date));
17442 //                    this.update();
17443                     e.preventDefault();
17444                     dateChanged = true;
17445                 }
17446                 break;
17447             case 38: // up
17448             case 40: // down
17449                 if (!this.keyboardNavigation) {
17450                     break;
17451                 }
17452                 dir = e.keyCode == 38 ? -1 : 1;
17453                 if (e.ctrlKey){
17454                     newDate = this.moveYear(this.date, dir);
17455                     newViewDate = this.moveYear(this.viewDate, dir);
17456                 } else if (e.shiftKey){
17457                     newDate = this.moveMonth(this.date, dir);
17458                     newViewDate = this.moveMonth(this.viewDate, dir);
17459                 } else {
17460                     newDate = new Date(this.date);
17461                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17462                     newViewDate = new Date(this.viewDate);
17463                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17464                 }
17465                 if (this.dateWithinRange(newDate)){
17466                     this.date = newDate;
17467                     this.viewDate = newViewDate;
17468                     this.setValue(this.formatDate(this.date));
17469 //                    this.update();
17470                     e.preventDefault();
17471                     dateChanged = true;
17472                 }
17473                 break;
17474             case 13: // enter
17475                 this.setValue(this.formatDate(this.date));
17476                 this.hide();
17477                 e.preventDefault();
17478                 break;
17479             case 9: // tab
17480                 this.setValue(this.formatDate(this.date));
17481                 this.hide();
17482                 break;
17483             case 16: // shift
17484             case 17: // ctrl
17485             case 18: // alt
17486                 break;
17487             default :
17488                 this.hide();
17489                 
17490         }
17491     },
17492     
17493     
17494     onClick: function(e) 
17495     {
17496         e.stopPropagation();
17497         e.preventDefault();
17498         
17499         var target = e.getTarget();
17500         
17501         if(target.nodeName.toLowerCase() === 'i'){
17502             target = Roo.get(target).dom.parentNode;
17503         }
17504         
17505         var nodeName = target.nodeName;
17506         var className = target.className;
17507         var html = target.innerHTML;
17508         //Roo.log(nodeName);
17509         
17510         switch(nodeName.toLowerCase()) {
17511             case 'th':
17512                 switch(className) {
17513                     case 'switch':
17514                         this.showMode(1);
17515                         break;
17516                     case 'prev':
17517                     case 'next':
17518                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17519                         switch(this.viewMode){
17520                                 case 0:
17521                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17522                                         break;
17523                                 case 1:
17524                                 case 2:
17525                                         this.viewDate = this.moveYear(this.viewDate, dir);
17526                                         break;
17527                         }
17528                         this.fill();
17529                         break;
17530                     case 'today':
17531                         var date = new Date();
17532                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17533 //                        this.fill()
17534                         this.setValue(this.formatDate(this.date));
17535                         
17536                         this.hide();
17537                         break;
17538                 }
17539                 break;
17540             case 'span':
17541                 if (className.indexOf('disabled') < 0) {
17542                     this.viewDate.setUTCDate(1);
17543                     if (className.indexOf('month') > -1) {
17544                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17545                     } else {
17546                         var year = parseInt(html, 10) || 0;
17547                         this.viewDate.setUTCFullYear(year);
17548                         
17549                     }
17550                     
17551                     if(this.singleMode){
17552                         this.setValue(this.formatDate(this.viewDate));
17553                         this.hide();
17554                         return;
17555                     }
17556                     
17557                     this.showMode(-1);
17558                     this.fill();
17559                 }
17560                 break;
17561                 
17562             case 'td':
17563                 //Roo.log(className);
17564                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17565                     var day = parseInt(html, 10) || 1;
17566                     var year = this.viewDate.getUTCFullYear(),
17567                         month = this.viewDate.getUTCMonth();
17568
17569                     if (className.indexOf('old') > -1) {
17570                         if(month === 0 ){
17571                             month = 11;
17572                             year -= 1;
17573                         }else{
17574                             month -= 1;
17575                         }
17576                     } else if (className.indexOf('new') > -1) {
17577                         if (month == 11) {
17578                             month = 0;
17579                             year += 1;
17580                         } else {
17581                             month += 1;
17582                         }
17583                     }
17584                     //Roo.log([year,month,day]);
17585                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17586                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17587 //                    this.fill();
17588                     //Roo.log(this.formatDate(this.date));
17589                     this.setValue(this.formatDate(this.date));
17590                     this.hide();
17591                 }
17592                 break;
17593         }
17594     },
17595     
17596     setStartDate: function(startDate)
17597     {
17598         this.startDate = startDate || -Infinity;
17599         if (this.startDate !== -Infinity) {
17600             this.startDate = this.parseDate(this.startDate);
17601         }
17602         this.update();
17603         this.updateNavArrows();
17604     },
17605
17606     setEndDate: function(endDate)
17607     {
17608         this.endDate = endDate || Infinity;
17609         if (this.endDate !== Infinity) {
17610             this.endDate = this.parseDate(this.endDate);
17611         }
17612         this.update();
17613         this.updateNavArrows();
17614     },
17615     
17616     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17617     {
17618         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17619         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17620             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17621         }
17622         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17623             return parseInt(d, 10);
17624         });
17625         this.update();
17626         this.updateNavArrows();
17627     },
17628     
17629     updateNavArrows: function() 
17630     {
17631         if(this.singleMode){
17632             return;
17633         }
17634         
17635         var d = new Date(this.viewDate),
17636         year = d.getUTCFullYear(),
17637         month = d.getUTCMonth();
17638         
17639         Roo.each(this.picker().select('.prev', true).elements, function(v){
17640             v.show();
17641             switch (this.viewMode) {
17642                 case 0:
17643
17644                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17645                         v.hide();
17646                     }
17647                     break;
17648                 case 1:
17649                 case 2:
17650                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17651                         v.hide();
17652                     }
17653                     break;
17654             }
17655         });
17656         
17657         Roo.each(this.picker().select('.next', true).elements, function(v){
17658             v.show();
17659             switch (this.viewMode) {
17660                 case 0:
17661
17662                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17663                         v.hide();
17664                     }
17665                     break;
17666                 case 1:
17667                 case 2:
17668                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17669                         v.hide();
17670                     }
17671                     break;
17672             }
17673         })
17674     },
17675     
17676     moveMonth: function(date, dir)
17677     {
17678         if (!dir) {
17679             return date;
17680         }
17681         var new_date = new Date(date.valueOf()),
17682         day = new_date.getUTCDate(),
17683         month = new_date.getUTCMonth(),
17684         mag = Math.abs(dir),
17685         new_month, test;
17686         dir = dir > 0 ? 1 : -1;
17687         if (mag == 1){
17688             test = dir == -1
17689             // If going back one month, make sure month is not current month
17690             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17691             ? function(){
17692                 return new_date.getUTCMonth() == month;
17693             }
17694             // If going forward one month, make sure month is as expected
17695             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17696             : function(){
17697                 return new_date.getUTCMonth() != new_month;
17698             };
17699             new_month = month + dir;
17700             new_date.setUTCMonth(new_month);
17701             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17702             if (new_month < 0 || new_month > 11) {
17703                 new_month = (new_month + 12) % 12;
17704             }
17705         } else {
17706             // For magnitudes >1, move one month at a time...
17707             for (var i=0; i<mag; i++) {
17708                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17709                 new_date = this.moveMonth(new_date, dir);
17710             }
17711             // ...then reset the day, keeping it in the new month
17712             new_month = new_date.getUTCMonth();
17713             new_date.setUTCDate(day);
17714             test = function(){
17715                 return new_month != new_date.getUTCMonth();
17716             };
17717         }
17718         // Common date-resetting loop -- if date is beyond end of month, make it
17719         // end of month
17720         while (test()){
17721             new_date.setUTCDate(--day);
17722             new_date.setUTCMonth(new_month);
17723         }
17724         return new_date;
17725     },
17726
17727     moveYear: function(date, dir)
17728     {
17729         return this.moveMonth(date, dir*12);
17730     },
17731
17732     dateWithinRange: function(date)
17733     {
17734         return date >= this.startDate && date <= this.endDate;
17735     },
17736
17737     
17738     remove: function() 
17739     {
17740         this.picker().remove();
17741     }
17742    
17743 });
17744
17745 Roo.apply(Roo.bootstrap.DateField,  {
17746     
17747     head : {
17748         tag: 'thead',
17749         cn: [
17750         {
17751             tag: 'tr',
17752             cn: [
17753             {
17754                 tag: 'th',
17755                 cls: 'prev',
17756                 html: '<i class="fa fa-arrow-left"/>'
17757             },
17758             {
17759                 tag: 'th',
17760                 cls: 'switch',
17761                 colspan: '5'
17762             },
17763             {
17764                 tag: 'th',
17765                 cls: 'next',
17766                 html: '<i class="fa fa-arrow-right"/>'
17767             }
17768
17769             ]
17770         }
17771         ]
17772     },
17773     
17774     content : {
17775         tag: 'tbody',
17776         cn: [
17777         {
17778             tag: 'tr',
17779             cn: [
17780             {
17781                 tag: 'td',
17782                 colspan: '7'
17783             }
17784             ]
17785         }
17786         ]
17787     },
17788     
17789     footer : {
17790         tag: 'tfoot',
17791         cn: [
17792         {
17793             tag: 'tr',
17794             cn: [
17795             {
17796                 tag: 'th',
17797                 colspan: '7',
17798                 cls: 'today'
17799             }
17800                     
17801             ]
17802         }
17803         ]
17804     },
17805     
17806     dates:{
17807         en: {
17808             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17809             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17810             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17811             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17812             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17813             today: "Today"
17814         }
17815     },
17816     
17817     modes: [
17818     {
17819         clsName: 'days',
17820         navFnc: 'Month',
17821         navStep: 1
17822     },
17823     {
17824         clsName: 'months',
17825         navFnc: 'FullYear',
17826         navStep: 1
17827     },
17828     {
17829         clsName: 'years',
17830         navFnc: 'FullYear',
17831         navStep: 10
17832     }]
17833 });
17834
17835 Roo.apply(Roo.bootstrap.DateField,  {
17836   
17837     template : {
17838         tag: 'div',
17839         cls: 'datepicker dropdown-menu roo-dynamic',
17840         cn: [
17841         {
17842             tag: 'div',
17843             cls: 'datepicker-days',
17844             cn: [
17845             {
17846                 tag: 'table',
17847                 cls: 'table-condensed',
17848                 cn:[
17849                 Roo.bootstrap.DateField.head,
17850                 {
17851                     tag: 'tbody'
17852                 },
17853                 Roo.bootstrap.DateField.footer
17854                 ]
17855             }
17856             ]
17857         },
17858         {
17859             tag: 'div',
17860             cls: 'datepicker-months',
17861             cn: [
17862             {
17863                 tag: 'table',
17864                 cls: 'table-condensed',
17865                 cn:[
17866                 Roo.bootstrap.DateField.head,
17867                 Roo.bootstrap.DateField.content,
17868                 Roo.bootstrap.DateField.footer
17869                 ]
17870             }
17871             ]
17872         },
17873         {
17874             tag: 'div',
17875             cls: 'datepicker-years',
17876             cn: [
17877             {
17878                 tag: 'table',
17879                 cls: 'table-condensed',
17880                 cn:[
17881                 Roo.bootstrap.DateField.head,
17882                 Roo.bootstrap.DateField.content,
17883                 Roo.bootstrap.DateField.footer
17884                 ]
17885             }
17886             ]
17887         }
17888         ]
17889     }
17890 });
17891
17892  
17893
17894  /*
17895  * - LGPL
17896  *
17897  * TimeField
17898  * 
17899  */
17900
17901 /**
17902  * @class Roo.bootstrap.TimeField
17903  * @extends Roo.bootstrap.Input
17904  * Bootstrap DateField class
17905  * 
17906  * 
17907  * @constructor
17908  * Create a new TimeField
17909  * @param {Object} config The config object
17910  */
17911
17912 Roo.bootstrap.TimeField = function(config){
17913     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17914     this.addEvents({
17915             /**
17916              * @event show
17917              * Fires when this field show.
17918              * @param {Roo.bootstrap.DateField} thisthis
17919              * @param {Mixed} date The date value
17920              */
17921             show : true,
17922             /**
17923              * @event show
17924              * Fires when this field hide.
17925              * @param {Roo.bootstrap.DateField} this
17926              * @param {Mixed} date The date value
17927              */
17928             hide : true,
17929             /**
17930              * @event select
17931              * Fires when select a date.
17932              * @param {Roo.bootstrap.DateField} this
17933              * @param {Mixed} date The date value
17934              */
17935             select : true
17936         });
17937 };
17938
17939 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17940     
17941     /**
17942      * @cfg {String} format
17943      * The default time format string which can be overriden for localization support.  The format must be
17944      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17945      */
17946     format : "H:i",
17947        
17948     onRender: function(ct, position)
17949     {
17950         
17951         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17952                 
17953         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17954         
17955         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17956         
17957         this.pop = this.picker().select('>.datepicker-time',true).first();
17958         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17959         
17960         this.picker().on('mousedown', this.onMousedown, this);
17961         this.picker().on('click', this.onClick, this);
17962         
17963         this.picker().addClass('datepicker-dropdown');
17964     
17965         this.fillTime();
17966         this.update();
17967             
17968         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17969         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17970         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17971         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17972         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17973         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17974
17975     },
17976     
17977     fireKey: function(e){
17978         if (!this.picker().isVisible()){
17979             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17980                 this.show();
17981             }
17982             return;
17983         }
17984
17985         e.preventDefault();
17986         
17987         switch(e.keyCode){
17988             case 27: // escape
17989                 this.hide();
17990                 break;
17991             case 37: // left
17992             case 39: // right
17993                 this.onTogglePeriod();
17994                 break;
17995             case 38: // up
17996                 this.onIncrementMinutes();
17997                 break;
17998             case 40: // down
17999                 this.onDecrementMinutes();
18000                 break;
18001             case 13: // enter
18002             case 9: // tab
18003                 this.setTime();
18004                 break;
18005         }
18006     },
18007     
18008     onClick: function(e) {
18009         e.stopPropagation();
18010         e.preventDefault();
18011     },
18012     
18013     picker : function()
18014     {
18015         return this.el.select('.datepicker', true).first();
18016     },
18017     
18018     fillTime: function()
18019     {    
18020         var time = this.pop.select('tbody', true).first();
18021         
18022         time.dom.innerHTML = '';
18023         
18024         time.createChild({
18025             tag: 'tr',
18026             cn: [
18027                 {
18028                     tag: 'td',
18029                     cn: [
18030                         {
18031                             tag: 'a',
18032                             href: '#',
18033                             cls: 'btn',
18034                             cn: [
18035                                 {
18036                                     tag: 'span',
18037                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18038                                 }
18039                             ]
18040                         } 
18041                     ]
18042                 },
18043                 {
18044                     tag: 'td',
18045                     cls: 'separator'
18046                 },
18047                 {
18048                     tag: 'td',
18049                     cn: [
18050                         {
18051                             tag: 'a',
18052                             href: '#',
18053                             cls: 'btn',
18054                             cn: [
18055                                 {
18056                                     tag: 'span',
18057                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18058                                 }
18059                             ]
18060                         }
18061                     ]
18062                 },
18063                 {
18064                     tag: 'td',
18065                     cls: 'separator'
18066                 }
18067             ]
18068         });
18069         
18070         time.createChild({
18071             tag: 'tr',
18072             cn: [
18073                 {
18074                     tag: 'td',
18075                     cn: [
18076                         {
18077                             tag: 'span',
18078                             cls: 'timepicker-hour',
18079                             html: '00'
18080                         }  
18081                     ]
18082                 },
18083                 {
18084                     tag: 'td',
18085                     cls: 'separator',
18086                     html: ':'
18087                 },
18088                 {
18089                     tag: 'td',
18090                     cn: [
18091                         {
18092                             tag: 'span',
18093                             cls: 'timepicker-minute',
18094                             html: '00'
18095                         }  
18096                     ]
18097                 },
18098                 {
18099                     tag: 'td',
18100                     cls: 'separator'
18101                 },
18102                 {
18103                     tag: 'td',
18104                     cn: [
18105                         {
18106                             tag: 'button',
18107                             type: 'button',
18108                             cls: 'btn btn-primary period',
18109                             html: 'AM'
18110                             
18111                         }
18112                     ]
18113                 }
18114             ]
18115         });
18116         
18117         time.createChild({
18118             tag: 'tr',
18119             cn: [
18120                 {
18121                     tag: 'td',
18122                     cn: [
18123                         {
18124                             tag: 'a',
18125                             href: '#',
18126                             cls: 'btn',
18127                             cn: [
18128                                 {
18129                                     tag: 'span',
18130                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18131                                 }
18132                             ]
18133                         }
18134                     ]
18135                 },
18136                 {
18137                     tag: 'td',
18138                     cls: 'separator'
18139                 },
18140                 {
18141                     tag: 'td',
18142                     cn: [
18143                         {
18144                             tag: 'a',
18145                             href: '#',
18146                             cls: 'btn',
18147                             cn: [
18148                                 {
18149                                     tag: 'span',
18150                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18151                                 }
18152                             ]
18153                         }
18154                     ]
18155                 },
18156                 {
18157                     tag: 'td',
18158                     cls: 'separator'
18159                 }
18160             ]
18161         });
18162         
18163     },
18164     
18165     update: function()
18166     {
18167         
18168         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18169         
18170         this.fill();
18171     },
18172     
18173     fill: function() 
18174     {
18175         var hours = this.time.getHours();
18176         var minutes = this.time.getMinutes();
18177         var period = 'AM';
18178         
18179         if(hours > 11){
18180             period = 'PM';
18181         }
18182         
18183         if(hours == 0){
18184             hours = 12;
18185         }
18186         
18187         
18188         if(hours > 12){
18189             hours = hours - 12;
18190         }
18191         
18192         if(hours < 10){
18193             hours = '0' + hours;
18194         }
18195         
18196         if(minutes < 10){
18197             minutes = '0' + minutes;
18198         }
18199         
18200         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18201         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18202         this.pop.select('button', true).first().dom.innerHTML = period;
18203         
18204     },
18205     
18206     place: function()
18207     {   
18208         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18209         
18210         var cls = ['bottom'];
18211         
18212         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18213             cls.pop();
18214             cls.push('top');
18215         }
18216         
18217         cls.push('right');
18218         
18219         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18220             cls.pop();
18221             cls.push('left');
18222         }
18223         
18224         this.picker().addClass(cls.join('-'));
18225         
18226         var _this = this;
18227         
18228         Roo.each(cls, function(c){
18229             if(c == 'bottom'){
18230                 _this.picker().setTop(_this.inputEl().getHeight());
18231                 return;
18232             }
18233             if(c == 'top'){
18234                 _this.picker().setTop(0 - _this.picker().getHeight());
18235                 return;
18236             }
18237             
18238             if(c == 'left'){
18239                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18240                 return;
18241             }
18242             if(c == 'right'){
18243                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18244                 return;
18245             }
18246         });
18247         
18248     },
18249   
18250     onFocus : function()
18251     {
18252         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18253         this.show();
18254     },
18255     
18256     onBlur : function()
18257     {
18258         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18259         this.hide();
18260     },
18261     
18262     show : function()
18263     {
18264         this.picker().show();
18265         this.pop.show();
18266         this.update();
18267         this.place();
18268         
18269         this.fireEvent('show', this, this.date);
18270     },
18271     
18272     hide : function()
18273     {
18274         this.picker().hide();
18275         this.pop.hide();
18276         
18277         this.fireEvent('hide', this, this.date);
18278     },
18279     
18280     setTime : function()
18281     {
18282         this.hide();
18283         this.setValue(this.time.format(this.format));
18284         
18285         this.fireEvent('select', this, this.date);
18286         
18287         
18288     },
18289     
18290     onMousedown: function(e){
18291         e.stopPropagation();
18292         e.preventDefault();
18293     },
18294     
18295     onIncrementHours: function()
18296     {
18297         Roo.log('onIncrementHours');
18298         this.time = this.time.add(Date.HOUR, 1);
18299         this.update();
18300         
18301     },
18302     
18303     onDecrementHours: function()
18304     {
18305         Roo.log('onDecrementHours');
18306         this.time = this.time.add(Date.HOUR, -1);
18307         this.update();
18308     },
18309     
18310     onIncrementMinutes: function()
18311     {
18312         Roo.log('onIncrementMinutes');
18313         this.time = this.time.add(Date.MINUTE, 1);
18314         this.update();
18315     },
18316     
18317     onDecrementMinutes: function()
18318     {
18319         Roo.log('onDecrementMinutes');
18320         this.time = this.time.add(Date.MINUTE, -1);
18321         this.update();
18322     },
18323     
18324     onTogglePeriod: function()
18325     {
18326         Roo.log('onTogglePeriod');
18327         this.time = this.time.add(Date.HOUR, 12);
18328         this.update();
18329     }
18330     
18331    
18332 });
18333
18334 Roo.apply(Roo.bootstrap.TimeField,  {
18335     
18336     content : {
18337         tag: 'tbody',
18338         cn: [
18339             {
18340                 tag: 'tr',
18341                 cn: [
18342                 {
18343                     tag: 'td',
18344                     colspan: '7'
18345                 }
18346                 ]
18347             }
18348         ]
18349     },
18350     
18351     footer : {
18352         tag: 'tfoot',
18353         cn: [
18354             {
18355                 tag: 'tr',
18356                 cn: [
18357                 {
18358                     tag: 'th',
18359                     colspan: '7',
18360                     cls: '',
18361                     cn: [
18362                         {
18363                             tag: 'button',
18364                             cls: 'btn btn-info ok',
18365                             html: 'OK'
18366                         }
18367                     ]
18368                 }
18369
18370                 ]
18371             }
18372         ]
18373     }
18374 });
18375
18376 Roo.apply(Roo.bootstrap.TimeField,  {
18377   
18378     template : {
18379         tag: 'div',
18380         cls: 'datepicker dropdown-menu',
18381         cn: [
18382             {
18383                 tag: 'div',
18384                 cls: 'datepicker-time',
18385                 cn: [
18386                 {
18387                     tag: 'table',
18388                     cls: 'table-condensed',
18389                     cn:[
18390                     Roo.bootstrap.TimeField.content,
18391                     Roo.bootstrap.TimeField.footer
18392                     ]
18393                 }
18394                 ]
18395             }
18396         ]
18397     }
18398 });
18399
18400  
18401
18402  /*
18403  * - LGPL
18404  *
18405  * MonthField
18406  * 
18407  */
18408
18409 /**
18410  * @class Roo.bootstrap.MonthField
18411  * @extends Roo.bootstrap.Input
18412  * Bootstrap MonthField class
18413  * 
18414  * @cfg {String} language default en
18415  * 
18416  * @constructor
18417  * Create a new MonthField
18418  * @param {Object} config The config object
18419  */
18420
18421 Roo.bootstrap.MonthField = function(config){
18422     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18423     
18424     this.addEvents({
18425         /**
18426          * @event show
18427          * Fires when this field show.
18428          * @param {Roo.bootstrap.MonthField} this
18429          * @param {Mixed} date The date value
18430          */
18431         show : true,
18432         /**
18433          * @event show
18434          * Fires when this field hide.
18435          * @param {Roo.bootstrap.MonthField} this
18436          * @param {Mixed} date The date value
18437          */
18438         hide : true,
18439         /**
18440          * @event select
18441          * Fires when select a date.
18442          * @param {Roo.bootstrap.MonthField} this
18443          * @param {String} oldvalue The old value
18444          * @param {String} newvalue The new value
18445          */
18446         select : true
18447     });
18448 };
18449
18450 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18451     
18452     onRender: function(ct, position)
18453     {
18454         
18455         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18456         
18457         this.language = this.language || 'en';
18458         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18459         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18460         
18461         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18462         this.isInline = false;
18463         this.isInput = true;
18464         this.component = this.el.select('.add-on', true).first() || false;
18465         this.component = (this.component && this.component.length === 0) ? false : this.component;
18466         this.hasInput = this.component && this.inputEL().length;
18467         
18468         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18469         
18470         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18471         
18472         this.picker().on('mousedown', this.onMousedown, this);
18473         this.picker().on('click', this.onClick, this);
18474         
18475         this.picker().addClass('datepicker-dropdown');
18476         
18477         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18478             v.setStyle('width', '189px');
18479         });
18480         
18481         this.fillMonths();
18482         
18483         this.update();
18484         
18485         if(this.isInline) {
18486             this.show();
18487         }
18488         
18489     },
18490     
18491     setValue: function(v, suppressEvent)
18492     {   
18493         var o = this.getValue();
18494         
18495         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18496         
18497         this.update();
18498
18499         if(suppressEvent !== true){
18500             this.fireEvent('select', this, o, v);
18501         }
18502         
18503     },
18504     
18505     getValue: function()
18506     {
18507         return this.value;
18508     },
18509     
18510     onClick: function(e) 
18511     {
18512         e.stopPropagation();
18513         e.preventDefault();
18514         
18515         var target = e.getTarget();
18516         
18517         if(target.nodeName.toLowerCase() === 'i'){
18518             target = Roo.get(target).dom.parentNode;
18519         }
18520         
18521         var nodeName = target.nodeName;
18522         var className = target.className;
18523         var html = target.innerHTML;
18524         
18525         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18526             return;
18527         }
18528         
18529         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18530         
18531         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18532         
18533         this.hide();
18534                         
18535     },
18536     
18537     picker : function()
18538     {
18539         return this.pickerEl;
18540     },
18541     
18542     fillMonths: function()
18543     {    
18544         var i = 0;
18545         var months = this.picker().select('>.datepicker-months td', true).first();
18546         
18547         months.dom.innerHTML = '';
18548         
18549         while (i < 12) {
18550             var month = {
18551                 tag: 'span',
18552                 cls: 'month',
18553                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18554             };
18555             
18556             months.createChild(month);
18557         }
18558         
18559     },
18560     
18561     update: function()
18562     {
18563         var _this = this;
18564         
18565         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18566             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18567         }
18568         
18569         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18570             e.removeClass('active');
18571             
18572             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18573                 e.addClass('active');
18574             }
18575         })
18576     },
18577     
18578     place: function()
18579     {
18580         if(this.isInline) {
18581             return;
18582         }
18583         
18584         this.picker().removeClass(['bottom', 'top']);
18585         
18586         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18587             /*
18588              * place to the top of element!
18589              *
18590              */
18591             
18592             this.picker().addClass('top');
18593             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18594             
18595             return;
18596         }
18597         
18598         this.picker().addClass('bottom');
18599         
18600         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18601     },
18602     
18603     onFocus : function()
18604     {
18605         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18606         this.show();
18607     },
18608     
18609     onBlur : function()
18610     {
18611         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18612         
18613         var d = this.inputEl().getValue();
18614         
18615         this.setValue(d);
18616                 
18617         this.hide();
18618     },
18619     
18620     show : function()
18621     {
18622         this.picker().show();
18623         this.picker().select('>.datepicker-months', true).first().show();
18624         this.update();
18625         this.place();
18626         
18627         this.fireEvent('show', this, this.date);
18628     },
18629     
18630     hide : function()
18631     {
18632         if(this.isInline) {
18633             return;
18634         }
18635         this.picker().hide();
18636         this.fireEvent('hide', this, this.date);
18637         
18638     },
18639     
18640     onMousedown: function(e)
18641     {
18642         e.stopPropagation();
18643         e.preventDefault();
18644     },
18645     
18646     keyup: function(e)
18647     {
18648         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18649         this.update();
18650     },
18651
18652     fireKey: function(e)
18653     {
18654         if (!this.picker().isVisible()){
18655             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18656                 this.show();
18657             }
18658             return;
18659         }
18660         
18661         var dir;
18662         
18663         switch(e.keyCode){
18664             case 27: // escape
18665                 this.hide();
18666                 e.preventDefault();
18667                 break;
18668             case 37: // left
18669             case 39: // right
18670                 dir = e.keyCode == 37 ? -1 : 1;
18671                 
18672                 this.vIndex = this.vIndex + dir;
18673                 
18674                 if(this.vIndex < 0){
18675                     this.vIndex = 0;
18676                 }
18677                 
18678                 if(this.vIndex > 11){
18679                     this.vIndex = 11;
18680                 }
18681                 
18682                 if(isNaN(this.vIndex)){
18683                     this.vIndex = 0;
18684                 }
18685                 
18686                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18687                 
18688                 break;
18689             case 38: // up
18690             case 40: // down
18691                 
18692                 dir = e.keyCode == 38 ? -1 : 1;
18693                 
18694                 this.vIndex = this.vIndex + dir * 4;
18695                 
18696                 if(this.vIndex < 0){
18697                     this.vIndex = 0;
18698                 }
18699                 
18700                 if(this.vIndex > 11){
18701                     this.vIndex = 11;
18702                 }
18703                 
18704                 if(isNaN(this.vIndex)){
18705                     this.vIndex = 0;
18706                 }
18707                 
18708                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18709                 break;
18710                 
18711             case 13: // enter
18712                 
18713                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18714                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18715                 }
18716                 
18717                 this.hide();
18718                 e.preventDefault();
18719                 break;
18720             case 9: // tab
18721                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18722                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18723                 }
18724                 this.hide();
18725                 break;
18726             case 16: // shift
18727             case 17: // ctrl
18728             case 18: // alt
18729                 break;
18730             default :
18731                 this.hide();
18732                 
18733         }
18734     },
18735     
18736     remove: function() 
18737     {
18738         this.picker().remove();
18739     }
18740    
18741 });
18742
18743 Roo.apply(Roo.bootstrap.MonthField,  {
18744     
18745     content : {
18746         tag: 'tbody',
18747         cn: [
18748         {
18749             tag: 'tr',
18750             cn: [
18751             {
18752                 tag: 'td',
18753                 colspan: '7'
18754             }
18755             ]
18756         }
18757         ]
18758     },
18759     
18760     dates:{
18761         en: {
18762             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18763             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18764         }
18765     }
18766 });
18767
18768 Roo.apply(Roo.bootstrap.MonthField,  {
18769   
18770     template : {
18771         tag: 'div',
18772         cls: 'datepicker dropdown-menu roo-dynamic',
18773         cn: [
18774             {
18775                 tag: 'div',
18776                 cls: 'datepicker-months',
18777                 cn: [
18778                 {
18779                     tag: 'table',
18780                     cls: 'table-condensed',
18781                     cn:[
18782                         Roo.bootstrap.DateField.content
18783                     ]
18784                 }
18785                 ]
18786             }
18787         ]
18788     }
18789 });
18790
18791  
18792
18793  
18794  /*
18795  * - LGPL
18796  *
18797  * CheckBox
18798  * 
18799  */
18800
18801 /**
18802  * @class Roo.bootstrap.CheckBox
18803  * @extends Roo.bootstrap.Input
18804  * Bootstrap CheckBox class
18805  * 
18806  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18807  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18808  * @cfg {String} boxLabel The text that appears beside the checkbox
18809  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18810  * @cfg {Boolean} checked initnal the element
18811  * @cfg {Boolean} inline inline the element (default false)
18812  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18813  * 
18814  * @constructor
18815  * Create a new CheckBox
18816  * @param {Object} config The config object
18817  */
18818
18819 Roo.bootstrap.CheckBox = function(config){
18820     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18821    
18822     this.addEvents({
18823         /**
18824         * @event check
18825         * Fires when the element is checked or unchecked.
18826         * @param {Roo.bootstrap.CheckBox} this This input
18827         * @param {Boolean} checked The new checked value
18828         */
18829        check : true
18830     });
18831     
18832 };
18833
18834 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18835   
18836     inputType: 'checkbox',
18837     inputValue: 1,
18838     valueOff: 0,
18839     boxLabel: false,
18840     checked: false,
18841     weight : false,
18842     inline: false,
18843     
18844     getAutoCreate : function()
18845     {
18846         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18847         
18848         var id = Roo.id();
18849         
18850         var cfg = {};
18851         
18852         cfg.cls = 'form-group ' + this.inputType; //input-group
18853         
18854         if(this.inline){
18855             cfg.cls += ' ' + this.inputType + '-inline';
18856         }
18857         
18858         var input =  {
18859             tag: 'input',
18860             id : id,
18861             type : this.inputType,
18862             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18863             cls : 'roo-' + this.inputType, //'form-box',
18864             placeholder : this.placeholder || ''
18865             
18866         };
18867         
18868         if (this.weight) { // Validity check?
18869             cfg.cls += " " + this.inputType + "-" + this.weight;
18870         }
18871         
18872         if (this.disabled) {
18873             input.disabled=true;
18874         }
18875         
18876         if(this.checked){
18877             input.checked = this.checked;
18878         }
18879         
18880         if (this.name) {
18881             input.name = this.name;
18882         }
18883         
18884         if (this.size) {
18885             input.cls += ' input-' + this.size;
18886         }
18887         
18888         var settings=this;
18889         
18890         ['xs','sm','md','lg'].map(function(size){
18891             if (settings[size]) {
18892                 cfg.cls += ' col-' + size + '-' + settings[size];
18893             }
18894         });
18895         
18896         var inputblock = input;
18897          
18898         if (this.before || this.after) {
18899             
18900             inputblock = {
18901                 cls : 'input-group',
18902                 cn :  [] 
18903             };
18904             
18905             if (this.before) {
18906                 inputblock.cn.push({
18907                     tag :'span',
18908                     cls : 'input-group-addon',
18909                     html : this.before
18910                 });
18911             }
18912             
18913             inputblock.cn.push(input);
18914             
18915             if (this.after) {
18916                 inputblock.cn.push({
18917                     tag :'span',
18918                     cls : 'input-group-addon',
18919                     html : this.after
18920                 });
18921             }
18922             
18923         }
18924         
18925         if (align ==='left' && this.fieldLabel.length) {
18926 //                Roo.log("left and has label");
18927                 cfg.cn = [
18928                     
18929                     {
18930                         tag: 'label',
18931                         'for' :  id,
18932                         cls : 'control-label col-md-' + this.labelWidth,
18933                         html : this.fieldLabel
18934                         
18935                     },
18936                     {
18937                         cls : "col-md-" + (12 - this.labelWidth), 
18938                         cn: [
18939                             inputblock
18940                         ]
18941                     }
18942                     
18943                 ];
18944         } else if ( this.fieldLabel.length) {
18945 //                Roo.log(" label");
18946                 cfg.cn = [
18947                    
18948                     {
18949                         tag: this.boxLabel ? 'span' : 'label',
18950                         'for': id,
18951                         cls: 'control-label box-input-label',
18952                         //cls : 'input-group-addon',
18953                         html : this.fieldLabel
18954                         
18955                     },
18956                     
18957                     inputblock
18958                     
18959                 ];
18960
18961         } else {
18962             
18963 //                Roo.log(" no label && no align");
18964                 cfg.cn = [  inputblock ] ;
18965                 
18966                 
18967         }
18968         
18969         if(this.boxLabel){
18970              var boxLabelCfg = {
18971                 tag: 'label',
18972                 //'for': id, // box label is handled by onclick - so no for...
18973                 cls: 'box-label',
18974                 html: this.boxLabel
18975             };
18976             
18977             if(this.tooltip){
18978                 boxLabelCfg.tooltip = this.tooltip;
18979             }
18980              
18981             cfg.cn.push(boxLabelCfg);
18982         }
18983         
18984         
18985        
18986         return cfg;
18987         
18988     },
18989     
18990     /**
18991      * return the real input element.
18992      */
18993     inputEl: function ()
18994     {
18995         return this.el.select('input.roo-' + this.inputType,true).first();
18996     },
18997     
18998     labelEl: function()
18999     {
19000         return this.el.select('label.control-label',true).first();
19001     },
19002     /* depricated... */
19003     
19004     label: function()
19005     {
19006         return this.labelEl();
19007     },
19008     
19009     boxLabelEl: function()
19010     {
19011         return this.el.select('label.box-label',true).first();
19012     },
19013     
19014     initEvents : function()
19015     {
19016 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19017         
19018         this.inputEl().on('click', this.onClick,  this);
19019         
19020         if (this.boxLabel) { 
19021             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19022         }
19023         
19024         this.startValue = this.getValue();
19025         
19026         if(this.groupId){
19027             Roo.bootstrap.CheckBox.register(this);
19028         }
19029     },
19030     
19031     onClick : function()
19032     {   
19033         this.setChecked(!this.checked);
19034     },
19035     
19036     setChecked : function(state,suppressEvent)
19037     {
19038         this.startValue = this.getValue();
19039         
19040         if(this.inputType == 'radio'){
19041             
19042             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19043                 e.dom.checked = false;
19044             });
19045             
19046             this.inputEl().dom.checked = true;
19047             
19048             this.inputEl().dom.value = this.inputValue;
19049             
19050             if(suppressEvent !== true){
19051                 this.fireEvent('check', this, true);
19052             }
19053             
19054             this.validate();
19055             
19056             return;
19057         }
19058         
19059         this.checked = state;
19060         
19061         this.inputEl().dom.checked = state;
19062         
19063         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19064         
19065         if(suppressEvent !== true){
19066             this.fireEvent('check', this, state);
19067         }
19068         
19069         this.validate();
19070     },
19071     
19072     getValue : function()
19073     {
19074         if(this.inputType == 'radio'){
19075             return this.getGroupValue();
19076         }
19077         
19078         return this.inputEl().getValue();
19079         
19080     },
19081     
19082     getGroupValue : function()
19083     {
19084         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19085             return '';
19086         }
19087         
19088         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19089     },
19090     
19091     setValue : function(v,suppressEvent)
19092     {
19093         if(this.inputType == 'radio'){
19094             this.setGroupValue(v, suppressEvent);
19095             return;
19096         }
19097         
19098         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19099         
19100         this.validate();
19101     },
19102     
19103     setGroupValue : function(v, suppressEvent)
19104     {
19105         this.startValue = this.getValue();
19106         
19107         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19108             e.dom.checked = false;
19109             
19110             if(e.dom.value == v){
19111                 e.dom.checked = true;
19112             }
19113         });
19114         
19115         if(suppressEvent !== true){
19116             this.fireEvent('check', this, true);
19117         }
19118
19119         this.validate();
19120         
19121         return;
19122     },
19123     
19124     validate : function()
19125     {
19126         if(
19127                 this.disabled || 
19128                 (this.inputType == 'radio' && this.validateRadio()) ||
19129                 (this.inputType == 'checkbox' && this.validateCheckbox())
19130         ){
19131             this.markValid();
19132             return true;
19133         }
19134         
19135         this.markInvalid();
19136         return false;
19137     },
19138     
19139     validateRadio : function()
19140     {
19141         var valid = false;
19142         
19143         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19144             if(!e.dom.checked){
19145                 return;
19146             }
19147             
19148             valid = true;
19149             
19150             return false;
19151         });
19152         
19153         return valid;
19154     },
19155     
19156     validateCheckbox : function()
19157     {
19158         if(!this.groupId){
19159             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19160         }
19161         
19162         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19163         
19164         if(!group){
19165             return false;
19166         }
19167         
19168         var r = false;
19169         
19170         for(var i in group){
19171             if(r){
19172                 break;
19173             }
19174             
19175             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19176         }
19177         
19178         return r;
19179     },
19180     
19181     /**
19182      * Mark this field as valid
19183      */
19184     markValid : function()
19185     {
19186         if(this.allowBlank){
19187             return;
19188         }
19189         
19190         var _this = this;
19191         
19192         this.fireEvent('valid', this);
19193         
19194         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19195         
19196         if(this.groupId){
19197             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19198         }
19199         
19200         if(label){
19201             label.markValid();
19202         }
19203         
19204         if(this.inputType == 'radio'){
19205             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19206                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19207                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19208             });
19209             
19210             return;
19211         }
19212         
19213         if(!this.groupId){
19214             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19215             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19216             return;
19217         }
19218         
19219         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19220             
19221         if(!group){
19222             return;
19223         }
19224         
19225         for(var i in group){
19226             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19227             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19228         }
19229     },
19230     
19231      /**
19232      * Mark this field as invalid
19233      * @param {String} msg The validation message
19234      */
19235     markInvalid : function(msg)
19236     {
19237         if(this.allowBlank){
19238             return;
19239         }
19240         
19241         var _this = this;
19242         
19243         this.fireEvent('invalid', this, msg);
19244         
19245         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19246         
19247         if(this.groupId){
19248             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19249         }
19250         
19251         if(label){
19252             label.markInvalid();
19253         }
19254             
19255         if(this.inputType == 'radio'){
19256             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19257                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19258                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19259             });
19260             
19261             return;
19262         }
19263         
19264         if(!this.groupId){
19265             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19266             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19267             return;
19268         }
19269         
19270         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19271         
19272         if(!group){
19273             return;
19274         }
19275         
19276         for(var i in group){
19277             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19278             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19279         }
19280         
19281     }
19282     
19283 });
19284
19285 Roo.apply(Roo.bootstrap.CheckBox, {
19286     
19287     groups: {},
19288     
19289      /**
19290     * register a CheckBox Group
19291     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19292     */
19293     register : function(checkbox)
19294     {
19295         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19296             this.groups[checkbox.groupId] = {};
19297         }
19298         
19299         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19300             return;
19301         }
19302         
19303         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19304         
19305     },
19306     /**
19307     * fetch a CheckBox Group based on the group ID
19308     * @param {string} the group ID
19309     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19310     */
19311     get: function(groupId) {
19312         if (typeof(this.groups[groupId]) == 'undefined') {
19313             return false;
19314         }
19315         
19316         return this.groups[groupId] ;
19317     }
19318     
19319     
19320 });
19321 /*
19322  * - LGPL
19323  *
19324  * Radio
19325  *
19326  *
19327  * not inline
19328  *<div class="radio">
19329   <label>
19330     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19331     Option one is this and that&mdash;be sure to include why it's great
19332   </label>
19333 </div>
19334  *
19335  *
19336  *inline
19337  *<span>
19338  *<label class="radio-inline">fieldLabel</label>
19339  *<label class="radio-inline">
19340   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19341 </label>
19342 <span>
19343  * 
19344  * 
19345  */
19346
19347 /**
19348  * @class Roo.bootstrap.Radio
19349  * @extends Roo.bootstrap.CheckBox
19350  * Bootstrap Radio class
19351
19352  * @constructor
19353  * Create a new Radio
19354  * @param {Object} config The config object
19355  */
19356
19357 Roo.bootstrap.Radio = function(config){
19358     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19359    
19360 };
19361
19362 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19363     
19364     inputType: 'radio',
19365     inputValue: '',
19366     valueOff: '',
19367     
19368     getAutoCreate : function()
19369     {
19370         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19371         align = align || 'left'; // default...
19372         
19373         
19374         
19375         var id = Roo.id();
19376         
19377         var cfg = {
19378                 tag : this.inline ? 'span' : 'div',
19379                 cls : '',
19380                 cn : []
19381         };
19382         
19383         var inline = this.inline ? ' radio-inline' : '';
19384         
19385         var lbl = {
19386                 tag: 'label' ,
19387                 // does not need for, as we wrap the input with it..
19388                 'for' : id,
19389                 cls : 'control-label box-label' + inline,
19390                 cn : []
19391         };
19392         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19393         
19394         var fieldLabel = {
19395             tag: 'label' ,
19396             //cls : 'control-label' + inline,
19397             html : this.fieldLabel,
19398             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19399         };
19400         
19401  
19402         
19403         
19404         var input =  {
19405             tag: 'input',
19406             id : id,
19407             type : this.inputType,
19408             //value : (!this.checked) ? this.valueOff : this.inputValue,
19409             value : this.inputValue,
19410             cls : 'roo-radio',
19411             placeholder : this.placeholder || '' // ?? needed????
19412             
19413         };
19414         if (this.weight) { // Validity check?
19415             input.cls += " radio-" + this.weight;
19416         }
19417         if (this.disabled) {
19418             input.disabled=true;
19419         }
19420         
19421         if(this.checked){
19422             input.checked = this.checked;
19423         }
19424         
19425         if (this.name) {
19426             input.name = this.name;
19427         }
19428         
19429         if (this.size) {
19430             input.cls += ' input-' + this.size;
19431         }
19432         
19433         //?? can span's inline have a width??
19434         
19435         var settings=this;
19436         ['xs','sm','md','lg'].map(function(size){
19437             if (settings[size]) {
19438                 cfg.cls += ' col-' + size + '-' + settings[size];
19439             }
19440         });
19441         
19442         var inputblock = input;
19443         
19444         if (this.before || this.after) {
19445             
19446             inputblock = {
19447                 cls : 'input-group',
19448                 tag : 'span',
19449                 cn :  [] 
19450             };
19451             if (this.before) {
19452                 inputblock.cn.push({
19453                     tag :'span',
19454                     cls : 'input-group-addon',
19455                     html : this.before
19456                 });
19457             }
19458             inputblock.cn.push(input);
19459             if (this.after) {
19460                 inputblock.cn.push({
19461                     tag :'span',
19462                     cls : 'input-group-addon',
19463                     html : this.after
19464                 });
19465             }
19466             
19467         };
19468         
19469         
19470         if (this.fieldLabel && this.fieldLabel.length) {
19471             cfg.cn.push(fieldLabel);
19472         }
19473        
19474         // normal bootstrap puts the input inside the label.
19475         // however with our styled version - it has to go after the input.
19476        
19477         //lbl.cn.push(inputblock);
19478         
19479         var lblwrap =  {
19480             tag: 'span',
19481             cls: 'radio' + inline,
19482             cn: [
19483                 inputblock,
19484                 lbl
19485             ]
19486         };
19487         
19488         cfg.cn.push( lblwrap);
19489         
19490         if(this.boxLabel){
19491             lbl.cn.push({
19492                 tag: 'span',
19493                 html: this.boxLabel
19494             })
19495         }
19496          
19497         
19498         return cfg;
19499         
19500     },
19501     
19502     initEvents : function()
19503     {
19504 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19505         
19506         this.inputEl().on('click', this.onClick,  this);
19507         if (this.boxLabel) {
19508             //Roo.log('find label');
19509             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19510         }
19511         
19512     },
19513     
19514     inputEl: function ()
19515     {
19516         return this.el.select('input.roo-radio',true).first();
19517     },
19518     onClick : function()
19519     {   
19520         Roo.log("click");
19521         this.setChecked(true);
19522     },
19523     
19524     setChecked : function(state,suppressEvent)
19525     {
19526         if(state){
19527             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19528                 v.dom.checked = false;
19529             });
19530         }
19531         Roo.log(this.inputEl().dom);
19532         this.checked = state;
19533         this.inputEl().dom.checked = state;
19534         
19535         if(suppressEvent !== true){
19536             this.fireEvent('check', this, state);
19537         }
19538         
19539         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19540         
19541     },
19542     
19543     getGroupValue : function()
19544     {
19545         var value = '';
19546         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19547             if(v.dom.checked == true){
19548                 value = v.dom.value;
19549             }
19550         });
19551         
19552         return value;
19553     },
19554     
19555     /**
19556      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19557      * @return {Mixed} value The field value
19558      */
19559     getValue : function(){
19560         return this.getGroupValue();
19561     }
19562     
19563 });
19564
19565  
19566 //<script type="text/javascript">
19567
19568 /*
19569  * Based  Ext JS Library 1.1.1
19570  * Copyright(c) 2006-2007, Ext JS, LLC.
19571  * LGPL
19572  *
19573  */
19574  
19575 /**
19576  * @class Roo.HtmlEditorCore
19577  * @extends Roo.Component
19578  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19579  *
19580  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19581  */
19582
19583 Roo.HtmlEditorCore = function(config){
19584     
19585     
19586     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19587     
19588     
19589     this.addEvents({
19590         /**
19591          * @event initialize
19592          * Fires when the editor is fully initialized (including the iframe)
19593          * @param {Roo.HtmlEditorCore} this
19594          */
19595         initialize: true,
19596         /**
19597          * @event activate
19598          * Fires when the editor is first receives the focus. Any insertion must wait
19599          * until after this event.
19600          * @param {Roo.HtmlEditorCore} this
19601          */
19602         activate: true,
19603          /**
19604          * @event beforesync
19605          * Fires before the textarea is updated with content from the editor iframe. Return false
19606          * to cancel the sync.
19607          * @param {Roo.HtmlEditorCore} this
19608          * @param {String} html
19609          */
19610         beforesync: true,
19611          /**
19612          * @event beforepush
19613          * Fires before the iframe editor is updated with content from the textarea. Return false
19614          * to cancel the push.
19615          * @param {Roo.HtmlEditorCore} this
19616          * @param {String} html
19617          */
19618         beforepush: true,
19619          /**
19620          * @event sync
19621          * Fires when the textarea is updated with content from the editor iframe.
19622          * @param {Roo.HtmlEditorCore} this
19623          * @param {String} html
19624          */
19625         sync: true,
19626          /**
19627          * @event push
19628          * Fires when the iframe editor is updated with content from the textarea.
19629          * @param {Roo.HtmlEditorCore} this
19630          * @param {String} html
19631          */
19632         push: true,
19633         
19634         /**
19635          * @event editorevent
19636          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19637          * @param {Roo.HtmlEditorCore} this
19638          */
19639         editorevent: true
19640         
19641     });
19642     
19643     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19644     
19645     // defaults : white / black...
19646     this.applyBlacklists();
19647     
19648     
19649     
19650 };
19651
19652
19653 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19654
19655
19656      /**
19657      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19658      */
19659     
19660     owner : false,
19661     
19662      /**
19663      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19664      *                        Roo.resizable.
19665      */
19666     resizable : false,
19667      /**
19668      * @cfg {Number} height (in pixels)
19669      */   
19670     height: 300,
19671    /**
19672      * @cfg {Number} width (in pixels)
19673      */   
19674     width: 500,
19675     
19676     /**
19677      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19678      * 
19679      */
19680     stylesheets: false,
19681     
19682     // id of frame..
19683     frameId: false,
19684     
19685     // private properties
19686     validationEvent : false,
19687     deferHeight: true,
19688     initialized : false,
19689     activated : false,
19690     sourceEditMode : false,
19691     onFocus : Roo.emptyFn,
19692     iframePad:3,
19693     hideMode:'offsets',
19694     
19695     clearUp: true,
19696     
19697     // blacklist + whitelisted elements..
19698     black: false,
19699     white: false,
19700      
19701     
19702
19703     /**
19704      * Protected method that will not generally be called directly. It
19705      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19706      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19707      */
19708     getDocMarkup : function(){
19709         // body styles..
19710         var st = '';
19711         
19712         // inherit styels from page...?? 
19713         if (this.stylesheets === false) {
19714             
19715             Roo.get(document.head).select('style').each(function(node) {
19716                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19717             });
19718             
19719             Roo.get(document.head).select('link').each(function(node) { 
19720                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19721             });
19722             
19723         } else if (!this.stylesheets.length) {
19724                 // simple..
19725                 st = '<style type="text/css">' +
19726                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19727                    '</style>';
19728         } else { 
19729             
19730         }
19731         
19732         st +=  '<style type="text/css">' +
19733             'IMG { cursor: pointer } ' +
19734         '</style>';
19735
19736         
19737         return '<html><head>' + st  +
19738             //<style type="text/css">' +
19739             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19740             //'</style>' +
19741             ' </head><body class="roo-htmleditor-body"></body></html>';
19742     },
19743
19744     // private
19745     onRender : function(ct, position)
19746     {
19747         var _t = this;
19748         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19749         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19750         
19751         
19752         this.el.dom.style.border = '0 none';
19753         this.el.dom.setAttribute('tabIndex', -1);
19754         this.el.addClass('x-hidden hide');
19755         
19756         
19757         
19758         if(Roo.isIE){ // fix IE 1px bogus margin
19759             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19760         }
19761        
19762         
19763         this.frameId = Roo.id();
19764         
19765          
19766         
19767         var iframe = this.owner.wrap.createChild({
19768             tag: 'iframe',
19769             cls: 'form-control', // bootstrap..
19770             id: this.frameId,
19771             name: this.frameId,
19772             frameBorder : 'no',
19773             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19774         }, this.el
19775         );
19776         
19777         
19778         this.iframe = iframe.dom;
19779
19780          this.assignDocWin();
19781         
19782         this.doc.designMode = 'on';
19783        
19784         this.doc.open();
19785         this.doc.write(this.getDocMarkup());
19786         this.doc.close();
19787
19788         
19789         var task = { // must defer to wait for browser to be ready
19790             run : function(){
19791                 //console.log("run task?" + this.doc.readyState);
19792                 this.assignDocWin();
19793                 if(this.doc.body || this.doc.readyState == 'complete'){
19794                     try {
19795                         this.doc.designMode="on";
19796                     } catch (e) {
19797                         return;
19798                     }
19799                     Roo.TaskMgr.stop(task);
19800                     this.initEditor.defer(10, this);
19801                 }
19802             },
19803             interval : 10,
19804             duration: 10000,
19805             scope: this
19806         };
19807         Roo.TaskMgr.start(task);
19808
19809     },
19810
19811     // private
19812     onResize : function(w, h)
19813     {
19814          Roo.log('resize: ' +w + ',' + h );
19815         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19816         if(!this.iframe){
19817             return;
19818         }
19819         if(typeof w == 'number'){
19820             
19821             this.iframe.style.width = w + 'px';
19822         }
19823         if(typeof h == 'number'){
19824             
19825             this.iframe.style.height = h + 'px';
19826             if(this.doc){
19827                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19828             }
19829         }
19830         
19831     },
19832
19833     /**
19834      * Toggles the editor between standard and source edit mode.
19835      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19836      */
19837     toggleSourceEdit : function(sourceEditMode){
19838         
19839         this.sourceEditMode = sourceEditMode === true;
19840         
19841         if(this.sourceEditMode){
19842  
19843             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19844             
19845         }else{
19846             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19847             //this.iframe.className = '';
19848             this.deferFocus();
19849         }
19850         //this.setSize(this.owner.wrap.getSize());
19851         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19852     },
19853
19854     
19855   
19856
19857     /**
19858      * Protected method that will not generally be called directly. If you need/want
19859      * custom HTML cleanup, this is the method you should override.
19860      * @param {String} html The HTML to be cleaned
19861      * return {String} The cleaned HTML
19862      */
19863     cleanHtml : function(html){
19864         html = String(html);
19865         if(html.length > 5){
19866             if(Roo.isSafari){ // strip safari nonsense
19867                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19868             }
19869         }
19870         if(html == '&nbsp;'){
19871             html = '';
19872         }
19873         return html;
19874     },
19875
19876     /**
19877      * HTML Editor -> Textarea
19878      * Protected method that will not generally be called directly. Syncs the contents
19879      * of the editor iframe with the textarea.
19880      */
19881     syncValue : function(){
19882         if(this.initialized){
19883             var bd = (this.doc.body || this.doc.documentElement);
19884             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19885             var html = bd.innerHTML;
19886             if(Roo.isSafari){
19887                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19888                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19889                 if(m && m[1]){
19890                     html = '<div style="'+m[0]+'">' + html + '</div>';
19891                 }
19892             }
19893             html = this.cleanHtml(html);
19894             // fix up the special chars.. normaly like back quotes in word...
19895             // however we do not want to do this with chinese..
19896             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19897                 var cc = b.charCodeAt();
19898                 if (
19899                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19900                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19901                     (cc >= 0xf900 && cc < 0xfb00 )
19902                 ) {
19903                         return b;
19904                 }
19905                 return "&#"+cc+";" 
19906             });
19907             if(this.owner.fireEvent('beforesync', this, html) !== false){
19908                 this.el.dom.value = html;
19909                 this.owner.fireEvent('sync', this, html);
19910             }
19911         }
19912     },
19913
19914     /**
19915      * Protected method that will not generally be called directly. Pushes the value of the textarea
19916      * into the iframe editor.
19917      */
19918     pushValue : function(){
19919         if(this.initialized){
19920             var v = this.el.dom.value.trim();
19921             
19922 //            if(v.length < 1){
19923 //                v = '&#160;';
19924 //            }
19925             
19926             if(this.owner.fireEvent('beforepush', this, v) !== false){
19927                 var d = (this.doc.body || this.doc.documentElement);
19928                 d.innerHTML = v;
19929                 this.cleanUpPaste();
19930                 this.el.dom.value = d.innerHTML;
19931                 this.owner.fireEvent('push', this, v);
19932             }
19933         }
19934     },
19935
19936     // private
19937     deferFocus : function(){
19938         this.focus.defer(10, this);
19939     },
19940
19941     // doc'ed in Field
19942     focus : function(){
19943         if(this.win && !this.sourceEditMode){
19944             this.win.focus();
19945         }else{
19946             this.el.focus();
19947         }
19948     },
19949     
19950     assignDocWin: function()
19951     {
19952         var iframe = this.iframe;
19953         
19954          if(Roo.isIE){
19955             this.doc = iframe.contentWindow.document;
19956             this.win = iframe.contentWindow;
19957         } else {
19958 //            if (!Roo.get(this.frameId)) {
19959 //                return;
19960 //            }
19961 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19962 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19963             
19964             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19965                 return;
19966             }
19967             
19968             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19969             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19970         }
19971     },
19972     
19973     // private
19974     initEditor : function(){
19975         //console.log("INIT EDITOR");
19976         this.assignDocWin();
19977         
19978         
19979         
19980         this.doc.designMode="on";
19981         this.doc.open();
19982         this.doc.write(this.getDocMarkup());
19983         this.doc.close();
19984         
19985         var dbody = (this.doc.body || this.doc.documentElement);
19986         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19987         // this copies styles from the containing element into thsi one..
19988         // not sure why we need all of this..
19989         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19990         
19991         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19992         //ss['background-attachment'] = 'fixed'; // w3c
19993         dbody.bgProperties = 'fixed'; // ie
19994         //Roo.DomHelper.applyStyles(dbody, ss);
19995         Roo.EventManager.on(this.doc, {
19996             //'mousedown': this.onEditorEvent,
19997             'mouseup': this.onEditorEvent,
19998             'dblclick': this.onEditorEvent,
19999             'click': this.onEditorEvent,
20000             'keyup': this.onEditorEvent,
20001             buffer:100,
20002             scope: this
20003         });
20004         if(Roo.isGecko){
20005             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20006         }
20007         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20008             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20009         }
20010         this.initialized = true;
20011
20012         this.owner.fireEvent('initialize', this);
20013         this.pushValue();
20014     },
20015
20016     // private
20017     onDestroy : function(){
20018         
20019         
20020         
20021         if(this.rendered){
20022             
20023             //for (var i =0; i < this.toolbars.length;i++) {
20024             //    // fixme - ask toolbars for heights?
20025             //    this.toolbars[i].onDestroy();
20026            // }
20027             
20028             //this.wrap.dom.innerHTML = '';
20029             //this.wrap.remove();
20030         }
20031     },
20032
20033     // private
20034     onFirstFocus : function(){
20035         
20036         this.assignDocWin();
20037         
20038         
20039         this.activated = true;
20040          
20041     
20042         if(Roo.isGecko){ // prevent silly gecko errors
20043             this.win.focus();
20044             var s = this.win.getSelection();
20045             if(!s.focusNode || s.focusNode.nodeType != 3){
20046                 var r = s.getRangeAt(0);
20047                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20048                 r.collapse(true);
20049                 this.deferFocus();
20050             }
20051             try{
20052                 this.execCmd('useCSS', true);
20053                 this.execCmd('styleWithCSS', false);
20054             }catch(e){}
20055         }
20056         this.owner.fireEvent('activate', this);
20057     },
20058
20059     // private
20060     adjustFont: function(btn){
20061         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20062         //if(Roo.isSafari){ // safari
20063         //    adjust *= 2;
20064        // }
20065         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20066         if(Roo.isSafari){ // safari
20067             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20068             v =  (v < 10) ? 10 : v;
20069             v =  (v > 48) ? 48 : v;
20070             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20071             
20072         }
20073         
20074         
20075         v = Math.max(1, v+adjust);
20076         
20077         this.execCmd('FontSize', v  );
20078     },
20079
20080     onEditorEvent : function(e)
20081     {
20082         this.owner.fireEvent('editorevent', this, e);
20083       //  this.updateToolbar();
20084         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20085     },
20086
20087     insertTag : function(tg)
20088     {
20089         // could be a bit smarter... -> wrap the current selected tRoo..
20090         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20091             
20092             range = this.createRange(this.getSelection());
20093             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20094             wrappingNode.appendChild(range.extractContents());
20095             range.insertNode(wrappingNode);
20096
20097             return;
20098             
20099             
20100             
20101         }
20102         this.execCmd("formatblock",   tg);
20103         
20104     },
20105     
20106     insertText : function(txt)
20107     {
20108         
20109         
20110         var range = this.createRange();
20111         range.deleteContents();
20112                //alert(Sender.getAttribute('label'));
20113                
20114         range.insertNode(this.doc.createTextNode(txt));
20115     } ,
20116     
20117      
20118
20119     /**
20120      * Executes a Midas editor command on the editor document and performs necessary focus and
20121      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20122      * @param {String} cmd The Midas command
20123      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20124      */
20125     relayCmd : function(cmd, value){
20126         this.win.focus();
20127         this.execCmd(cmd, value);
20128         this.owner.fireEvent('editorevent', this);
20129         //this.updateToolbar();
20130         this.owner.deferFocus();
20131     },
20132
20133     /**
20134      * Executes a Midas editor command directly on the editor document.
20135      * For visual commands, you should use {@link #relayCmd} instead.
20136      * <b>This should only be called after the editor is initialized.</b>
20137      * @param {String} cmd The Midas command
20138      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20139      */
20140     execCmd : function(cmd, value){
20141         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20142         this.syncValue();
20143     },
20144  
20145  
20146    
20147     /**
20148      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20149      * to insert tRoo.
20150      * @param {String} text | dom node.. 
20151      */
20152     insertAtCursor : function(text)
20153     {
20154         
20155         
20156         
20157         if(!this.activated){
20158             return;
20159         }
20160         /*
20161         if(Roo.isIE){
20162             this.win.focus();
20163             var r = this.doc.selection.createRange();
20164             if(r){
20165                 r.collapse(true);
20166                 r.pasteHTML(text);
20167                 this.syncValue();
20168                 this.deferFocus();
20169             
20170             }
20171             return;
20172         }
20173         */
20174         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20175             this.win.focus();
20176             
20177             
20178             // from jquery ui (MIT licenced)
20179             var range, node;
20180             var win = this.win;
20181             
20182             if (win.getSelection && win.getSelection().getRangeAt) {
20183                 range = win.getSelection().getRangeAt(0);
20184                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20185                 range.insertNode(node);
20186             } else if (win.document.selection && win.document.selection.createRange) {
20187                 // no firefox support
20188                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20189                 win.document.selection.createRange().pasteHTML(txt);
20190             } else {
20191                 // no firefox support
20192                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20193                 this.execCmd('InsertHTML', txt);
20194             } 
20195             
20196             this.syncValue();
20197             
20198             this.deferFocus();
20199         }
20200     },
20201  // private
20202     mozKeyPress : function(e){
20203         if(e.ctrlKey){
20204             var c = e.getCharCode(), cmd;
20205           
20206             if(c > 0){
20207                 c = String.fromCharCode(c).toLowerCase();
20208                 switch(c){
20209                     case 'b':
20210                         cmd = 'bold';
20211                         break;
20212                     case 'i':
20213                         cmd = 'italic';
20214                         break;
20215                     
20216                     case 'u':
20217                         cmd = 'underline';
20218                         break;
20219                     
20220                     case 'v':
20221                         this.cleanUpPaste.defer(100, this);
20222                         return;
20223                         
20224                 }
20225                 if(cmd){
20226                     this.win.focus();
20227                     this.execCmd(cmd);
20228                     this.deferFocus();
20229                     e.preventDefault();
20230                 }
20231                 
20232             }
20233         }
20234     },
20235
20236     // private
20237     fixKeys : function(){ // load time branching for fastest keydown performance
20238         if(Roo.isIE){
20239             return function(e){
20240                 var k = e.getKey(), r;
20241                 if(k == e.TAB){
20242                     e.stopEvent();
20243                     r = this.doc.selection.createRange();
20244                     if(r){
20245                         r.collapse(true);
20246                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20247                         this.deferFocus();
20248                     }
20249                     return;
20250                 }
20251                 
20252                 if(k == e.ENTER){
20253                     r = this.doc.selection.createRange();
20254                     if(r){
20255                         var target = r.parentElement();
20256                         if(!target || target.tagName.toLowerCase() != 'li'){
20257                             e.stopEvent();
20258                             r.pasteHTML('<br />');
20259                             r.collapse(false);
20260                             r.select();
20261                         }
20262                     }
20263                 }
20264                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20265                     this.cleanUpPaste.defer(100, this);
20266                     return;
20267                 }
20268                 
20269                 
20270             };
20271         }else if(Roo.isOpera){
20272             return function(e){
20273                 var k = e.getKey();
20274                 if(k == e.TAB){
20275                     e.stopEvent();
20276                     this.win.focus();
20277                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20278                     this.deferFocus();
20279                 }
20280                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20281                     this.cleanUpPaste.defer(100, this);
20282                     return;
20283                 }
20284                 
20285             };
20286         }else if(Roo.isSafari){
20287             return function(e){
20288                 var k = e.getKey();
20289                 
20290                 if(k == e.TAB){
20291                     e.stopEvent();
20292                     this.execCmd('InsertText','\t');
20293                     this.deferFocus();
20294                     return;
20295                 }
20296                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20297                     this.cleanUpPaste.defer(100, this);
20298                     return;
20299                 }
20300                 
20301              };
20302         }
20303     }(),
20304     
20305     getAllAncestors: function()
20306     {
20307         var p = this.getSelectedNode();
20308         var a = [];
20309         if (!p) {
20310             a.push(p); // push blank onto stack..
20311             p = this.getParentElement();
20312         }
20313         
20314         
20315         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20316             a.push(p);
20317             p = p.parentNode;
20318         }
20319         a.push(this.doc.body);
20320         return a;
20321     },
20322     lastSel : false,
20323     lastSelNode : false,
20324     
20325     
20326     getSelection : function() 
20327     {
20328         this.assignDocWin();
20329         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20330     },
20331     
20332     getSelectedNode: function() 
20333     {
20334         // this may only work on Gecko!!!
20335         
20336         // should we cache this!!!!
20337         
20338         
20339         
20340          
20341         var range = this.createRange(this.getSelection()).cloneRange();
20342         
20343         if (Roo.isIE) {
20344             var parent = range.parentElement();
20345             while (true) {
20346                 var testRange = range.duplicate();
20347                 testRange.moveToElementText(parent);
20348                 if (testRange.inRange(range)) {
20349                     break;
20350                 }
20351                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20352                     break;
20353                 }
20354                 parent = parent.parentElement;
20355             }
20356             return parent;
20357         }
20358         
20359         // is ancestor a text element.
20360         var ac =  range.commonAncestorContainer;
20361         if (ac.nodeType == 3) {
20362             ac = ac.parentNode;
20363         }
20364         
20365         var ar = ac.childNodes;
20366          
20367         var nodes = [];
20368         var other_nodes = [];
20369         var has_other_nodes = false;
20370         for (var i=0;i<ar.length;i++) {
20371             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20372                 continue;
20373             }
20374             // fullly contained node.
20375             
20376             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20377                 nodes.push(ar[i]);
20378                 continue;
20379             }
20380             
20381             // probably selected..
20382             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20383                 other_nodes.push(ar[i]);
20384                 continue;
20385             }
20386             // outer..
20387             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20388                 continue;
20389             }
20390             
20391             
20392             has_other_nodes = true;
20393         }
20394         if (!nodes.length && other_nodes.length) {
20395             nodes= other_nodes;
20396         }
20397         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20398             return false;
20399         }
20400         
20401         return nodes[0];
20402     },
20403     createRange: function(sel)
20404     {
20405         // this has strange effects when using with 
20406         // top toolbar - not sure if it's a great idea.
20407         //this.editor.contentWindow.focus();
20408         if (typeof sel != "undefined") {
20409             try {
20410                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20411             } catch(e) {
20412                 return this.doc.createRange();
20413             }
20414         } else {
20415             return this.doc.createRange();
20416         }
20417     },
20418     getParentElement: function()
20419     {
20420         
20421         this.assignDocWin();
20422         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20423         
20424         var range = this.createRange(sel);
20425          
20426         try {
20427             var p = range.commonAncestorContainer;
20428             while (p.nodeType == 3) { // text node
20429                 p = p.parentNode;
20430             }
20431             return p;
20432         } catch (e) {
20433             return null;
20434         }
20435     
20436     },
20437     /***
20438      *
20439      * Range intersection.. the hard stuff...
20440      *  '-1' = before
20441      *  '0' = hits..
20442      *  '1' = after.
20443      *         [ -- selected range --- ]
20444      *   [fail]                        [fail]
20445      *
20446      *    basically..
20447      *      if end is before start or  hits it. fail.
20448      *      if start is after end or hits it fail.
20449      *
20450      *   if either hits (but other is outside. - then it's not 
20451      *   
20452      *    
20453      **/
20454     
20455     
20456     // @see http://www.thismuchiknow.co.uk/?p=64.
20457     rangeIntersectsNode : function(range, node)
20458     {
20459         var nodeRange = node.ownerDocument.createRange();
20460         try {
20461             nodeRange.selectNode(node);
20462         } catch (e) {
20463             nodeRange.selectNodeContents(node);
20464         }
20465     
20466         var rangeStartRange = range.cloneRange();
20467         rangeStartRange.collapse(true);
20468     
20469         var rangeEndRange = range.cloneRange();
20470         rangeEndRange.collapse(false);
20471     
20472         var nodeStartRange = nodeRange.cloneRange();
20473         nodeStartRange.collapse(true);
20474     
20475         var nodeEndRange = nodeRange.cloneRange();
20476         nodeEndRange.collapse(false);
20477     
20478         return rangeStartRange.compareBoundaryPoints(
20479                  Range.START_TO_START, nodeEndRange) == -1 &&
20480                rangeEndRange.compareBoundaryPoints(
20481                  Range.START_TO_START, nodeStartRange) == 1;
20482         
20483          
20484     },
20485     rangeCompareNode : function(range, node)
20486     {
20487         var nodeRange = node.ownerDocument.createRange();
20488         try {
20489             nodeRange.selectNode(node);
20490         } catch (e) {
20491             nodeRange.selectNodeContents(node);
20492         }
20493         
20494         
20495         range.collapse(true);
20496     
20497         nodeRange.collapse(true);
20498      
20499         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20500         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20501          
20502         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20503         
20504         var nodeIsBefore   =  ss == 1;
20505         var nodeIsAfter    = ee == -1;
20506         
20507         if (nodeIsBefore && nodeIsAfter) {
20508             return 0; // outer
20509         }
20510         if (!nodeIsBefore && nodeIsAfter) {
20511             return 1; //right trailed.
20512         }
20513         
20514         if (nodeIsBefore && !nodeIsAfter) {
20515             return 2;  // left trailed.
20516         }
20517         // fully contined.
20518         return 3;
20519     },
20520
20521     // private? - in a new class?
20522     cleanUpPaste :  function()
20523     {
20524         // cleans up the whole document..
20525         Roo.log('cleanuppaste');
20526         
20527         this.cleanUpChildren(this.doc.body);
20528         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20529         if (clean != this.doc.body.innerHTML) {
20530             this.doc.body.innerHTML = clean;
20531         }
20532         
20533     },
20534     
20535     cleanWordChars : function(input) {// change the chars to hex code
20536         var he = Roo.HtmlEditorCore;
20537         
20538         var output = input;
20539         Roo.each(he.swapCodes, function(sw) { 
20540             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20541             
20542             output = output.replace(swapper, sw[1]);
20543         });
20544         
20545         return output;
20546     },
20547     
20548     
20549     cleanUpChildren : function (n)
20550     {
20551         if (!n.childNodes.length) {
20552             return;
20553         }
20554         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20555            this.cleanUpChild(n.childNodes[i]);
20556         }
20557     },
20558     
20559     
20560         
20561     
20562     cleanUpChild : function (node)
20563     {
20564         var ed = this;
20565         //console.log(node);
20566         if (node.nodeName == "#text") {
20567             // clean up silly Windows -- stuff?
20568             return; 
20569         }
20570         if (node.nodeName == "#comment") {
20571             node.parentNode.removeChild(node);
20572             // clean up silly Windows -- stuff?
20573             return; 
20574         }
20575         var lcname = node.tagName.toLowerCase();
20576         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20577         // whitelist of tags..
20578         
20579         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20580             // remove node.
20581             node.parentNode.removeChild(node);
20582             return;
20583             
20584         }
20585         
20586         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20587         
20588         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20589         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20590         
20591         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20592         //    remove_keep_children = true;
20593         //}
20594         
20595         if (remove_keep_children) {
20596             this.cleanUpChildren(node);
20597             // inserts everything just before this node...
20598             while (node.childNodes.length) {
20599                 var cn = node.childNodes[0];
20600                 node.removeChild(cn);
20601                 node.parentNode.insertBefore(cn, node);
20602             }
20603             node.parentNode.removeChild(node);
20604             return;
20605         }
20606         
20607         if (!node.attributes || !node.attributes.length) {
20608             this.cleanUpChildren(node);
20609             return;
20610         }
20611         
20612         function cleanAttr(n,v)
20613         {
20614             
20615             if (v.match(/^\./) || v.match(/^\//)) {
20616                 return;
20617             }
20618             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20619                 return;
20620             }
20621             if (v.match(/^#/)) {
20622                 return;
20623             }
20624 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20625             node.removeAttribute(n);
20626             
20627         }
20628         
20629         var cwhite = this.cwhite;
20630         var cblack = this.cblack;
20631             
20632         function cleanStyle(n,v)
20633         {
20634             if (v.match(/expression/)) { //XSS?? should we even bother..
20635                 node.removeAttribute(n);
20636                 return;
20637             }
20638             
20639             var parts = v.split(/;/);
20640             var clean = [];
20641             
20642             Roo.each(parts, function(p) {
20643                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20644                 if (!p.length) {
20645                     return true;
20646                 }
20647                 var l = p.split(':').shift().replace(/\s+/g,'');
20648                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20649                 
20650                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20651 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20652                     //node.removeAttribute(n);
20653                     return true;
20654                 }
20655                 //Roo.log()
20656                 // only allow 'c whitelisted system attributes'
20657                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20658 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20659                     //node.removeAttribute(n);
20660                     return true;
20661                 }
20662                 
20663                 
20664                  
20665                 
20666                 clean.push(p);
20667                 return true;
20668             });
20669             if (clean.length) { 
20670                 node.setAttribute(n, clean.join(';'));
20671             } else {
20672                 node.removeAttribute(n);
20673             }
20674             
20675         }
20676         
20677         
20678         for (var i = node.attributes.length-1; i > -1 ; i--) {
20679             var a = node.attributes[i];
20680             //console.log(a);
20681             
20682             if (a.name.toLowerCase().substr(0,2)=='on')  {
20683                 node.removeAttribute(a.name);
20684                 continue;
20685             }
20686             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20687                 node.removeAttribute(a.name);
20688                 continue;
20689             }
20690             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20691                 cleanAttr(a.name,a.value); // fixme..
20692                 continue;
20693             }
20694             if (a.name == 'style') {
20695                 cleanStyle(a.name,a.value);
20696                 continue;
20697             }
20698             /// clean up MS crap..
20699             // tecnically this should be a list of valid class'es..
20700             
20701             
20702             if (a.name == 'class') {
20703                 if (a.value.match(/^Mso/)) {
20704                     node.className = '';
20705                 }
20706                 
20707                 if (a.value.match(/body/)) {
20708                     node.className = '';
20709                 }
20710                 continue;
20711             }
20712             
20713             // style cleanup!?
20714             // class cleanup?
20715             
20716         }
20717         
20718         
20719         this.cleanUpChildren(node);
20720         
20721         
20722     },
20723     
20724     /**
20725      * Clean up MS wordisms...
20726      */
20727     cleanWord : function(node)
20728     {
20729         
20730         
20731         if (!node) {
20732             this.cleanWord(this.doc.body);
20733             return;
20734         }
20735         if (node.nodeName == "#text") {
20736             // clean up silly Windows -- stuff?
20737             return; 
20738         }
20739         if (node.nodeName == "#comment") {
20740             node.parentNode.removeChild(node);
20741             // clean up silly Windows -- stuff?
20742             return; 
20743         }
20744         
20745         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20746             node.parentNode.removeChild(node);
20747             return;
20748         }
20749         
20750         // remove - but keep children..
20751         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20752             while (node.childNodes.length) {
20753                 var cn = node.childNodes[0];
20754                 node.removeChild(cn);
20755                 node.parentNode.insertBefore(cn, node);
20756             }
20757             node.parentNode.removeChild(node);
20758             this.iterateChildren(node, this.cleanWord);
20759             return;
20760         }
20761         // clean styles
20762         if (node.className.length) {
20763             
20764             var cn = node.className.split(/\W+/);
20765             var cna = [];
20766             Roo.each(cn, function(cls) {
20767                 if (cls.match(/Mso[a-zA-Z]+/)) {
20768                     return;
20769                 }
20770                 cna.push(cls);
20771             });
20772             node.className = cna.length ? cna.join(' ') : '';
20773             if (!cna.length) {
20774                 node.removeAttribute("class");
20775             }
20776         }
20777         
20778         if (node.hasAttribute("lang")) {
20779             node.removeAttribute("lang");
20780         }
20781         
20782         if (node.hasAttribute("style")) {
20783             
20784             var styles = node.getAttribute("style").split(";");
20785             var nstyle = [];
20786             Roo.each(styles, function(s) {
20787                 if (!s.match(/:/)) {
20788                     return;
20789                 }
20790                 var kv = s.split(":");
20791                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20792                     return;
20793                 }
20794                 // what ever is left... we allow.
20795                 nstyle.push(s);
20796             });
20797             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20798             if (!nstyle.length) {
20799                 node.removeAttribute('style');
20800             }
20801         }
20802         this.iterateChildren(node, this.cleanWord);
20803         
20804         
20805         
20806     },
20807     /**
20808      * iterateChildren of a Node, calling fn each time, using this as the scole..
20809      * @param {DomNode} node node to iterate children of.
20810      * @param {Function} fn method of this class to call on each item.
20811      */
20812     iterateChildren : function(node, fn)
20813     {
20814         if (!node.childNodes.length) {
20815                 return;
20816         }
20817         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20818            fn.call(this, node.childNodes[i])
20819         }
20820     },
20821     
20822     
20823     /**
20824      * cleanTableWidths.
20825      *
20826      * Quite often pasting from word etc.. results in tables with column and widths.
20827      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20828      *
20829      */
20830     cleanTableWidths : function(node)
20831     {
20832          
20833          
20834         if (!node) {
20835             this.cleanTableWidths(this.doc.body);
20836             return;
20837         }
20838         
20839         // ignore list...
20840         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20841             return; 
20842         }
20843         Roo.log(node.tagName);
20844         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20845             this.iterateChildren(node, this.cleanTableWidths);
20846             return;
20847         }
20848         if (node.hasAttribute('width')) {
20849             node.removeAttribute('width');
20850         }
20851         
20852          
20853         if (node.hasAttribute("style")) {
20854             // pretty basic...
20855             
20856             var styles = node.getAttribute("style").split(";");
20857             var nstyle = [];
20858             Roo.each(styles, function(s) {
20859                 if (!s.match(/:/)) {
20860                     return;
20861                 }
20862                 var kv = s.split(":");
20863                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20864                     return;
20865                 }
20866                 // what ever is left... we allow.
20867                 nstyle.push(s);
20868             });
20869             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20870             if (!nstyle.length) {
20871                 node.removeAttribute('style');
20872             }
20873         }
20874         
20875         this.iterateChildren(node, this.cleanTableWidths);
20876         
20877         
20878     },
20879     
20880     
20881     
20882     
20883     domToHTML : function(currentElement, depth, nopadtext) {
20884         
20885         depth = depth || 0;
20886         nopadtext = nopadtext || false;
20887     
20888         if (!currentElement) {
20889             return this.domToHTML(this.doc.body);
20890         }
20891         
20892         //Roo.log(currentElement);
20893         var j;
20894         var allText = false;
20895         var nodeName = currentElement.nodeName;
20896         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20897         
20898         if  (nodeName == '#text') {
20899             
20900             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20901         }
20902         
20903         
20904         var ret = '';
20905         if (nodeName != 'BODY') {
20906              
20907             var i = 0;
20908             // Prints the node tagName, such as <A>, <IMG>, etc
20909             if (tagName) {
20910                 var attr = [];
20911                 for(i = 0; i < currentElement.attributes.length;i++) {
20912                     // quoting?
20913                     var aname = currentElement.attributes.item(i).name;
20914                     if (!currentElement.attributes.item(i).value.length) {
20915                         continue;
20916                     }
20917                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20918                 }
20919                 
20920                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20921             } 
20922             else {
20923                 
20924                 // eack
20925             }
20926         } else {
20927             tagName = false;
20928         }
20929         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20930             return ret;
20931         }
20932         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20933             nopadtext = true;
20934         }
20935         
20936         
20937         // Traverse the tree
20938         i = 0;
20939         var currentElementChild = currentElement.childNodes.item(i);
20940         var allText = true;
20941         var innerHTML  = '';
20942         lastnode = '';
20943         while (currentElementChild) {
20944             // Formatting code (indent the tree so it looks nice on the screen)
20945             var nopad = nopadtext;
20946             if (lastnode == 'SPAN') {
20947                 nopad  = true;
20948             }
20949             // text
20950             if  (currentElementChild.nodeName == '#text') {
20951                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20952                 toadd = nopadtext ? toadd : toadd.trim();
20953                 if (!nopad && toadd.length > 80) {
20954                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20955                 }
20956                 innerHTML  += toadd;
20957                 
20958                 i++;
20959                 currentElementChild = currentElement.childNodes.item(i);
20960                 lastNode = '';
20961                 continue;
20962             }
20963             allText = false;
20964             
20965             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20966                 
20967             // Recursively traverse the tree structure of the child node
20968             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20969             lastnode = currentElementChild.nodeName;
20970             i++;
20971             currentElementChild=currentElement.childNodes.item(i);
20972         }
20973         
20974         ret += innerHTML;
20975         
20976         if (!allText) {
20977                 // The remaining code is mostly for formatting the tree
20978             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20979         }
20980         
20981         
20982         if (tagName) {
20983             ret+= "</"+tagName+">";
20984         }
20985         return ret;
20986         
20987     },
20988         
20989     applyBlacklists : function()
20990     {
20991         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20992         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20993         
20994         this.white = [];
20995         this.black = [];
20996         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20997             if (b.indexOf(tag) > -1) {
20998                 return;
20999             }
21000             this.white.push(tag);
21001             
21002         }, this);
21003         
21004         Roo.each(w, function(tag) {
21005             if (b.indexOf(tag) > -1) {
21006                 return;
21007             }
21008             if (this.white.indexOf(tag) > -1) {
21009                 return;
21010             }
21011             this.white.push(tag);
21012             
21013         }, this);
21014         
21015         
21016         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21017             if (w.indexOf(tag) > -1) {
21018                 return;
21019             }
21020             this.black.push(tag);
21021             
21022         }, this);
21023         
21024         Roo.each(b, function(tag) {
21025             if (w.indexOf(tag) > -1) {
21026                 return;
21027             }
21028             if (this.black.indexOf(tag) > -1) {
21029                 return;
21030             }
21031             this.black.push(tag);
21032             
21033         }, this);
21034         
21035         
21036         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21037         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21038         
21039         this.cwhite = [];
21040         this.cblack = [];
21041         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21042             if (b.indexOf(tag) > -1) {
21043                 return;
21044             }
21045             this.cwhite.push(tag);
21046             
21047         }, this);
21048         
21049         Roo.each(w, function(tag) {
21050             if (b.indexOf(tag) > -1) {
21051                 return;
21052             }
21053             if (this.cwhite.indexOf(tag) > -1) {
21054                 return;
21055             }
21056             this.cwhite.push(tag);
21057             
21058         }, this);
21059         
21060         
21061         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21062             if (w.indexOf(tag) > -1) {
21063                 return;
21064             }
21065             this.cblack.push(tag);
21066             
21067         }, this);
21068         
21069         Roo.each(b, function(tag) {
21070             if (w.indexOf(tag) > -1) {
21071                 return;
21072             }
21073             if (this.cblack.indexOf(tag) > -1) {
21074                 return;
21075             }
21076             this.cblack.push(tag);
21077             
21078         }, this);
21079     },
21080     
21081     setStylesheets : function(stylesheets)
21082     {
21083         if(typeof(stylesheets) == 'string'){
21084             Roo.get(this.iframe.contentDocument.head).createChild({
21085                 tag : 'link',
21086                 rel : 'stylesheet',
21087                 type : 'text/css',
21088                 href : stylesheets
21089             });
21090             
21091             return;
21092         }
21093         var _this = this;
21094      
21095         Roo.each(stylesheets, function(s) {
21096             if(!s.length){
21097                 return;
21098             }
21099             
21100             Roo.get(_this.iframe.contentDocument.head).createChild({
21101                 tag : 'link',
21102                 rel : 'stylesheet',
21103                 type : 'text/css',
21104                 href : s
21105             });
21106         });
21107
21108         
21109     },
21110     
21111     removeStylesheets : function()
21112     {
21113         var _this = this;
21114         
21115         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21116             s.remove();
21117         });
21118     }
21119     
21120     // hide stuff that is not compatible
21121     /**
21122      * @event blur
21123      * @hide
21124      */
21125     /**
21126      * @event change
21127      * @hide
21128      */
21129     /**
21130      * @event focus
21131      * @hide
21132      */
21133     /**
21134      * @event specialkey
21135      * @hide
21136      */
21137     /**
21138      * @cfg {String} fieldClass @hide
21139      */
21140     /**
21141      * @cfg {String} focusClass @hide
21142      */
21143     /**
21144      * @cfg {String} autoCreate @hide
21145      */
21146     /**
21147      * @cfg {String} inputType @hide
21148      */
21149     /**
21150      * @cfg {String} invalidClass @hide
21151      */
21152     /**
21153      * @cfg {String} invalidText @hide
21154      */
21155     /**
21156      * @cfg {String} msgFx @hide
21157      */
21158     /**
21159      * @cfg {String} validateOnBlur @hide
21160      */
21161 });
21162
21163 Roo.HtmlEditorCore.white = [
21164         'area', 'br', 'img', 'input', 'hr', 'wbr',
21165         
21166        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21167        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21168        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21169        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21170        'table',   'ul',         'xmp', 
21171        
21172        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21173       'thead',   'tr', 
21174      
21175       'dir', 'menu', 'ol', 'ul', 'dl',
21176        
21177       'embed',  'object'
21178 ];
21179
21180
21181 Roo.HtmlEditorCore.black = [
21182     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21183         'applet', // 
21184         'base',   'basefont', 'bgsound', 'blink',  'body', 
21185         'frame',  'frameset', 'head',    'html',   'ilayer', 
21186         'iframe', 'layer',  'link',     'meta',    'object',   
21187         'script', 'style' ,'title',  'xml' // clean later..
21188 ];
21189 Roo.HtmlEditorCore.clean = [
21190     'script', 'style', 'title', 'xml'
21191 ];
21192 Roo.HtmlEditorCore.remove = [
21193     'font'
21194 ];
21195 // attributes..
21196
21197 Roo.HtmlEditorCore.ablack = [
21198     'on'
21199 ];
21200     
21201 Roo.HtmlEditorCore.aclean = [ 
21202     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21203 ];
21204
21205 // protocols..
21206 Roo.HtmlEditorCore.pwhite= [
21207         'http',  'https',  'mailto'
21208 ];
21209
21210 // white listed style attributes.
21211 Roo.HtmlEditorCore.cwhite= [
21212       //  'text-align', /// default is to allow most things..
21213       
21214          
21215 //        'font-size'//??
21216 ];
21217
21218 // black listed style attributes.
21219 Roo.HtmlEditorCore.cblack= [
21220       //  'font-size' -- this can be set by the project 
21221 ];
21222
21223
21224 Roo.HtmlEditorCore.swapCodes   =[ 
21225     [    8211, "--" ], 
21226     [    8212, "--" ], 
21227     [    8216,  "'" ],  
21228     [    8217, "'" ],  
21229     [    8220, '"' ],  
21230     [    8221, '"' ],  
21231     [    8226, "*" ],  
21232     [    8230, "..." ]
21233 ]; 
21234
21235     /*
21236  * - LGPL
21237  *
21238  * HtmlEditor
21239  * 
21240  */
21241
21242 /**
21243  * @class Roo.bootstrap.HtmlEditor
21244  * @extends Roo.bootstrap.TextArea
21245  * Bootstrap HtmlEditor class
21246
21247  * @constructor
21248  * Create a new HtmlEditor
21249  * @param {Object} config The config object
21250  */
21251
21252 Roo.bootstrap.HtmlEditor = function(config){
21253     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21254     if (!this.toolbars) {
21255         this.toolbars = [];
21256     }
21257     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21258     this.addEvents({
21259             /**
21260              * @event initialize
21261              * Fires when the editor is fully initialized (including the iframe)
21262              * @param {HtmlEditor} this
21263              */
21264             initialize: true,
21265             /**
21266              * @event activate
21267              * Fires when the editor is first receives the focus. Any insertion must wait
21268              * until after this event.
21269              * @param {HtmlEditor} this
21270              */
21271             activate: true,
21272              /**
21273              * @event beforesync
21274              * Fires before the textarea is updated with content from the editor iframe. Return false
21275              * to cancel the sync.
21276              * @param {HtmlEditor} this
21277              * @param {String} html
21278              */
21279             beforesync: true,
21280              /**
21281              * @event beforepush
21282              * Fires before the iframe editor is updated with content from the textarea. Return false
21283              * to cancel the push.
21284              * @param {HtmlEditor} this
21285              * @param {String} html
21286              */
21287             beforepush: true,
21288              /**
21289              * @event sync
21290              * Fires when the textarea is updated with content from the editor iframe.
21291              * @param {HtmlEditor} this
21292              * @param {String} html
21293              */
21294             sync: true,
21295              /**
21296              * @event push
21297              * Fires when the iframe editor is updated with content from the textarea.
21298              * @param {HtmlEditor} this
21299              * @param {String} html
21300              */
21301             push: true,
21302              /**
21303              * @event editmodechange
21304              * Fires when the editor switches edit modes
21305              * @param {HtmlEditor} this
21306              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21307              */
21308             editmodechange: true,
21309             /**
21310              * @event editorevent
21311              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21312              * @param {HtmlEditor} this
21313              */
21314             editorevent: true,
21315             /**
21316              * @event firstfocus
21317              * Fires when on first focus - needed by toolbars..
21318              * @param {HtmlEditor} this
21319              */
21320             firstfocus: true,
21321             /**
21322              * @event autosave
21323              * Auto save the htmlEditor value as a file into Events
21324              * @param {HtmlEditor} this
21325              */
21326             autosave: true,
21327             /**
21328              * @event savedpreview
21329              * preview the saved version of htmlEditor
21330              * @param {HtmlEditor} this
21331              */
21332             savedpreview: true
21333         });
21334 };
21335
21336
21337 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21338     
21339     
21340       /**
21341      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21342      */
21343     toolbars : false,
21344    
21345      /**
21346      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21347      *                        Roo.resizable.
21348      */
21349     resizable : false,
21350      /**
21351      * @cfg {Number} height (in pixels)
21352      */   
21353     height: 300,
21354    /**
21355      * @cfg {Number} width (in pixels)
21356      */   
21357     width: false,
21358     
21359     /**
21360      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21361      * 
21362      */
21363     stylesheets: false,
21364     
21365     // id of frame..
21366     frameId: false,
21367     
21368     // private properties
21369     validationEvent : false,
21370     deferHeight: true,
21371     initialized : false,
21372     activated : false,
21373     
21374     onFocus : Roo.emptyFn,
21375     iframePad:3,
21376     hideMode:'offsets',
21377     
21378     
21379     tbContainer : false,
21380     
21381     toolbarContainer :function() {
21382         return this.wrap.select('.x-html-editor-tb',true).first();
21383     },
21384
21385     /**
21386      * Protected method that will not generally be called directly. It
21387      * is called when the editor creates its toolbar. Override this method if you need to
21388      * add custom toolbar buttons.
21389      * @param {HtmlEditor} editor
21390      */
21391     createToolbar : function(){
21392         
21393         Roo.log("create toolbars");
21394         
21395         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21396         this.toolbars[0].render(this.toolbarContainer());
21397         
21398         return;
21399         
21400 //        if (!editor.toolbars || !editor.toolbars.length) {
21401 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21402 //        }
21403 //        
21404 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21405 //            editor.toolbars[i] = Roo.factory(
21406 //                    typeof(editor.toolbars[i]) == 'string' ?
21407 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21408 //                Roo.bootstrap.HtmlEditor);
21409 //            editor.toolbars[i].init(editor);
21410 //        }
21411     },
21412
21413      
21414     // private
21415     onRender : function(ct, position)
21416     {
21417        // Roo.log("Call onRender: " + this.xtype);
21418         var _t = this;
21419         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21420       
21421         this.wrap = this.inputEl().wrap({
21422             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21423         });
21424         
21425         this.editorcore.onRender(ct, position);
21426          
21427         if (this.resizable) {
21428             this.resizeEl = new Roo.Resizable(this.wrap, {
21429                 pinned : true,
21430                 wrap: true,
21431                 dynamic : true,
21432                 minHeight : this.height,
21433                 height: this.height,
21434                 handles : this.resizable,
21435                 width: this.width,
21436                 listeners : {
21437                     resize : function(r, w, h) {
21438                         _t.onResize(w,h); // -something
21439                     }
21440                 }
21441             });
21442             
21443         }
21444         this.createToolbar(this);
21445        
21446         
21447         if(!this.width && this.resizable){
21448             this.setSize(this.wrap.getSize());
21449         }
21450         if (this.resizeEl) {
21451             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21452             // should trigger onReize..
21453         }
21454         
21455     },
21456
21457     // private
21458     onResize : function(w, h)
21459     {
21460         Roo.log('resize: ' +w + ',' + h );
21461         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21462         var ew = false;
21463         var eh = false;
21464         
21465         if(this.inputEl() ){
21466             if(typeof w == 'number'){
21467                 var aw = w - this.wrap.getFrameWidth('lr');
21468                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21469                 ew = aw;
21470             }
21471             if(typeof h == 'number'){
21472                  var tbh = -11;  // fixme it needs to tool bar size!
21473                 for (var i =0; i < this.toolbars.length;i++) {
21474                     // fixme - ask toolbars for heights?
21475                     tbh += this.toolbars[i].el.getHeight();
21476                     //if (this.toolbars[i].footer) {
21477                     //    tbh += this.toolbars[i].footer.el.getHeight();
21478                     //}
21479                 }
21480               
21481                 
21482                 
21483                 
21484                 
21485                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21486                 ah -= 5; // knock a few pixes off for look..
21487                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21488                 var eh = ah;
21489             }
21490         }
21491         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21492         this.editorcore.onResize(ew,eh);
21493         
21494     },
21495
21496     /**
21497      * Toggles the editor between standard and source edit mode.
21498      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21499      */
21500     toggleSourceEdit : function(sourceEditMode)
21501     {
21502         this.editorcore.toggleSourceEdit(sourceEditMode);
21503         
21504         if(this.editorcore.sourceEditMode){
21505             Roo.log('editor - showing textarea');
21506             
21507 //            Roo.log('in');
21508 //            Roo.log(this.syncValue());
21509             this.syncValue();
21510             this.inputEl().removeClass(['hide', 'x-hidden']);
21511             this.inputEl().dom.removeAttribute('tabIndex');
21512             this.inputEl().focus();
21513         }else{
21514             Roo.log('editor - hiding textarea');
21515 //            Roo.log('out')
21516 //            Roo.log(this.pushValue()); 
21517             this.pushValue();
21518             
21519             this.inputEl().addClass(['hide', 'x-hidden']);
21520             this.inputEl().dom.setAttribute('tabIndex', -1);
21521             //this.deferFocus();
21522         }
21523          
21524         if(this.resizable){
21525             this.setSize(this.wrap.getSize());
21526         }
21527         
21528         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21529     },
21530  
21531     // private (for BoxComponent)
21532     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21533
21534     // private (for BoxComponent)
21535     getResizeEl : function(){
21536         return this.wrap;
21537     },
21538
21539     // private (for BoxComponent)
21540     getPositionEl : function(){
21541         return this.wrap;
21542     },
21543
21544     // private
21545     initEvents : function(){
21546         this.originalValue = this.getValue();
21547     },
21548
21549 //    /**
21550 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21551 //     * @method
21552 //     */
21553 //    markInvalid : Roo.emptyFn,
21554 //    /**
21555 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21556 //     * @method
21557 //     */
21558 //    clearInvalid : Roo.emptyFn,
21559
21560     setValue : function(v){
21561         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21562         this.editorcore.pushValue();
21563     },
21564
21565      
21566     // private
21567     deferFocus : function(){
21568         this.focus.defer(10, this);
21569     },
21570
21571     // doc'ed in Field
21572     focus : function(){
21573         this.editorcore.focus();
21574         
21575     },
21576       
21577
21578     // private
21579     onDestroy : function(){
21580         
21581         
21582         
21583         if(this.rendered){
21584             
21585             for (var i =0; i < this.toolbars.length;i++) {
21586                 // fixme - ask toolbars for heights?
21587                 this.toolbars[i].onDestroy();
21588             }
21589             
21590             this.wrap.dom.innerHTML = '';
21591             this.wrap.remove();
21592         }
21593     },
21594
21595     // private
21596     onFirstFocus : function(){
21597         //Roo.log("onFirstFocus");
21598         this.editorcore.onFirstFocus();
21599          for (var i =0; i < this.toolbars.length;i++) {
21600             this.toolbars[i].onFirstFocus();
21601         }
21602         
21603     },
21604     
21605     // private
21606     syncValue : function()
21607     {   
21608         this.editorcore.syncValue();
21609     },
21610     
21611     pushValue : function()
21612     {   
21613         this.editorcore.pushValue();
21614     }
21615      
21616     
21617     // hide stuff that is not compatible
21618     /**
21619      * @event blur
21620      * @hide
21621      */
21622     /**
21623      * @event change
21624      * @hide
21625      */
21626     /**
21627      * @event focus
21628      * @hide
21629      */
21630     /**
21631      * @event specialkey
21632      * @hide
21633      */
21634     /**
21635      * @cfg {String} fieldClass @hide
21636      */
21637     /**
21638      * @cfg {String} focusClass @hide
21639      */
21640     /**
21641      * @cfg {String} autoCreate @hide
21642      */
21643     /**
21644      * @cfg {String} inputType @hide
21645      */
21646     /**
21647      * @cfg {String} invalidClass @hide
21648      */
21649     /**
21650      * @cfg {String} invalidText @hide
21651      */
21652     /**
21653      * @cfg {String} msgFx @hide
21654      */
21655     /**
21656      * @cfg {String} validateOnBlur @hide
21657      */
21658 });
21659  
21660     
21661    
21662    
21663    
21664       
21665 Roo.namespace('Roo.bootstrap.htmleditor');
21666 /**
21667  * @class Roo.bootstrap.HtmlEditorToolbar1
21668  * Basic Toolbar
21669  * 
21670  * Usage:
21671  *
21672  new Roo.bootstrap.HtmlEditor({
21673     ....
21674     toolbars : [
21675         new Roo.bootstrap.HtmlEditorToolbar1({
21676             disable : { fonts: 1 , format: 1, ..., ... , ...],
21677             btns : [ .... ]
21678         })
21679     }
21680      
21681  * 
21682  * @cfg {Object} disable List of elements to disable..
21683  * @cfg {Array} btns List of additional buttons.
21684  * 
21685  * 
21686  * NEEDS Extra CSS? 
21687  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21688  */
21689  
21690 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21691 {
21692     
21693     Roo.apply(this, config);
21694     
21695     // default disabled, based on 'good practice'..
21696     this.disable = this.disable || {};
21697     Roo.applyIf(this.disable, {
21698         fontSize : true,
21699         colors : true,
21700         specialElements : true
21701     });
21702     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21703     
21704     this.editor = config.editor;
21705     this.editorcore = config.editor.editorcore;
21706     
21707     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21708     
21709     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21710     // dont call parent... till later.
21711 }
21712 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21713      
21714     bar : true,
21715     
21716     editor : false,
21717     editorcore : false,
21718     
21719     
21720     formats : [
21721         "p" ,  
21722         "h1","h2","h3","h4","h5","h6", 
21723         "pre", "code", 
21724         "abbr", "acronym", "address", "cite", "samp", "var",
21725         'div','span'
21726     ],
21727     
21728     onRender : function(ct, position)
21729     {
21730        // Roo.log("Call onRender: " + this.xtype);
21731         
21732        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21733        Roo.log(this.el);
21734        this.el.dom.style.marginBottom = '0';
21735        var _this = this;
21736        var editorcore = this.editorcore;
21737        var editor= this.editor;
21738        
21739        var children = [];
21740        var btn = function(id,cmd , toggle, handler){
21741        
21742             var  event = toggle ? 'toggle' : 'click';
21743        
21744             var a = {
21745                 size : 'sm',
21746                 xtype: 'Button',
21747                 xns: Roo.bootstrap,
21748                 glyphicon : id,
21749                 cmd : id || cmd,
21750                 enableToggle:toggle !== false,
21751                 //html : 'submit'
21752                 pressed : toggle ? false : null,
21753                 listeners : {}
21754             };
21755             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21756                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21757             };
21758             children.push(a);
21759             return a;
21760        }
21761         
21762         var style = {
21763                 xtype: 'Button',
21764                 size : 'sm',
21765                 xns: Roo.bootstrap,
21766                 glyphicon : 'font',
21767                 //html : 'submit'
21768                 menu : {
21769                     xtype: 'Menu',
21770                     xns: Roo.bootstrap,
21771                     items:  []
21772                 }
21773         };
21774         Roo.each(this.formats, function(f) {
21775             style.menu.items.push({
21776                 xtype :'MenuItem',
21777                 xns: Roo.bootstrap,
21778                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21779                 tagname : f,
21780                 listeners : {
21781                     click : function()
21782                     {
21783                         editorcore.insertTag(this.tagname);
21784                         editor.focus();
21785                     }
21786                 }
21787                 
21788             });
21789         });
21790          children.push(style);   
21791             
21792             
21793         btn('bold',false,true);
21794         btn('italic',false,true);
21795         btn('align-left', 'justifyleft',true);
21796         btn('align-center', 'justifycenter',true);
21797         btn('align-right' , 'justifyright',true);
21798         btn('link', false, false, function(btn) {
21799             //Roo.log("create link?");
21800             var url = prompt(this.createLinkText, this.defaultLinkValue);
21801             if(url && url != 'http:/'+'/'){
21802                 this.editorcore.relayCmd('createlink', url);
21803             }
21804         }),
21805         btn('list','insertunorderedlist',true);
21806         btn('pencil', false,true, function(btn){
21807                 Roo.log(this);
21808                 
21809                 this.toggleSourceEdit(btn.pressed);
21810         });
21811         /*
21812         var cog = {
21813                 xtype: 'Button',
21814                 size : 'sm',
21815                 xns: Roo.bootstrap,
21816                 glyphicon : 'cog',
21817                 //html : 'submit'
21818                 menu : {
21819                     xtype: 'Menu',
21820                     xns: Roo.bootstrap,
21821                     items:  []
21822                 }
21823         };
21824         
21825         cog.menu.items.push({
21826             xtype :'MenuItem',
21827             xns: Roo.bootstrap,
21828             html : Clean styles,
21829             tagname : f,
21830             listeners : {
21831                 click : function()
21832                 {
21833                     editorcore.insertTag(this.tagname);
21834                     editor.focus();
21835                 }
21836             }
21837             
21838         });
21839        */
21840         
21841          
21842        this.xtype = 'NavSimplebar';
21843         
21844         for(var i=0;i< children.length;i++) {
21845             
21846             this.buttons.add(this.addxtypeChild(children[i]));
21847             
21848         }
21849         
21850         editor.on('editorevent', this.updateToolbar, this);
21851     },
21852     onBtnClick : function(id)
21853     {
21854        this.editorcore.relayCmd(id);
21855        this.editorcore.focus();
21856     },
21857     
21858     /**
21859      * Protected method that will not generally be called directly. It triggers
21860      * a toolbar update by reading the markup state of the current selection in the editor.
21861      */
21862     updateToolbar: function(){
21863
21864         if(!this.editorcore.activated){
21865             this.editor.onFirstFocus(); // is this neeed?
21866             return;
21867         }
21868
21869         var btns = this.buttons; 
21870         var doc = this.editorcore.doc;
21871         btns.get('bold').setActive(doc.queryCommandState('bold'));
21872         btns.get('italic').setActive(doc.queryCommandState('italic'));
21873         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21874         
21875         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21876         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21877         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21878         
21879         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21880         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21881          /*
21882         
21883         var ans = this.editorcore.getAllAncestors();
21884         if (this.formatCombo) {
21885             
21886             
21887             var store = this.formatCombo.store;
21888             this.formatCombo.setValue("");
21889             for (var i =0; i < ans.length;i++) {
21890                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21891                     // select it..
21892                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21893                     break;
21894                 }
21895             }
21896         }
21897         
21898         
21899         
21900         // hides menus... - so this cant be on a menu...
21901         Roo.bootstrap.MenuMgr.hideAll();
21902         */
21903         Roo.bootstrap.MenuMgr.hideAll();
21904         //this.editorsyncValue();
21905     },
21906     onFirstFocus: function() {
21907         this.buttons.each(function(item){
21908            item.enable();
21909         });
21910     },
21911     toggleSourceEdit : function(sourceEditMode){
21912         
21913           
21914         if(sourceEditMode){
21915             Roo.log("disabling buttons");
21916            this.buttons.each( function(item){
21917                 if(item.cmd != 'pencil'){
21918                     item.disable();
21919                 }
21920             });
21921           
21922         }else{
21923             Roo.log("enabling buttons");
21924             if(this.editorcore.initialized){
21925                 this.buttons.each( function(item){
21926                     item.enable();
21927                 });
21928             }
21929             
21930         }
21931         Roo.log("calling toggole on editor");
21932         // tell the editor that it's been pressed..
21933         this.editor.toggleSourceEdit(sourceEditMode);
21934        
21935     }
21936 });
21937
21938
21939
21940
21941
21942 /**
21943  * @class Roo.bootstrap.Table.AbstractSelectionModel
21944  * @extends Roo.util.Observable
21945  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21946  * implemented by descendant classes.  This class should not be directly instantiated.
21947  * @constructor
21948  */
21949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21950     this.locked = false;
21951     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21952 };
21953
21954
21955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21956     /** @ignore Called by the grid automatically. Do not call directly. */
21957     init : function(grid){
21958         this.grid = grid;
21959         this.initEvents();
21960     },
21961
21962     /**
21963      * Locks the selections.
21964      */
21965     lock : function(){
21966         this.locked = true;
21967     },
21968
21969     /**
21970      * Unlocks the selections.
21971      */
21972     unlock : function(){
21973         this.locked = false;
21974     },
21975
21976     /**
21977      * Returns true if the selections are locked.
21978      * @return {Boolean}
21979      */
21980     isLocked : function(){
21981         return this.locked;
21982     }
21983 });
21984 /**
21985  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21986  * @class Roo.bootstrap.Table.RowSelectionModel
21987  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21988  * It supports multiple selections and keyboard selection/navigation. 
21989  * @constructor
21990  * @param {Object} config
21991  */
21992
21993 Roo.bootstrap.Table.RowSelectionModel = function(config){
21994     Roo.apply(this, config);
21995     this.selections = new Roo.util.MixedCollection(false, function(o){
21996         return o.id;
21997     });
21998
21999     this.last = false;
22000     this.lastActive = false;
22001
22002     this.addEvents({
22003         /**
22004              * @event selectionchange
22005              * Fires when the selection changes
22006              * @param {SelectionModel} this
22007              */
22008             "selectionchange" : true,
22009         /**
22010              * @event afterselectionchange
22011              * Fires after the selection changes (eg. by key press or clicking)
22012              * @param {SelectionModel} this
22013              */
22014             "afterselectionchange" : true,
22015         /**
22016              * @event beforerowselect
22017              * Fires when a row is selected being selected, return false to cancel.
22018              * @param {SelectionModel} this
22019              * @param {Number} rowIndex The selected index
22020              * @param {Boolean} keepExisting False if other selections will be cleared
22021              */
22022             "beforerowselect" : true,
22023         /**
22024              * @event rowselect
22025              * Fires when a row is selected.
22026              * @param {SelectionModel} this
22027              * @param {Number} rowIndex The selected index
22028              * @param {Roo.data.Record} r The record
22029              */
22030             "rowselect" : true,
22031         /**
22032              * @event rowdeselect
22033              * Fires when a row is deselected.
22034              * @param {SelectionModel} this
22035              * @param {Number} rowIndex The selected index
22036              */
22037         "rowdeselect" : true
22038     });
22039     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22040     this.locked = false;
22041 };
22042
22043 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22044     /**
22045      * @cfg {Boolean} singleSelect
22046      * True to allow selection of only one row at a time (defaults to false)
22047      */
22048     singleSelect : false,
22049
22050     // private
22051     initEvents : function(){
22052
22053         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22054             this.grid.on("mousedown", this.handleMouseDown, this);
22055         }else{ // allow click to work like normal
22056             this.grid.on("rowclick", this.handleDragableRowClick, this);
22057         }
22058
22059         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22060             "up" : function(e){
22061                 if(!e.shiftKey){
22062                     this.selectPrevious(e.shiftKey);
22063                 }else if(this.last !== false && this.lastActive !== false){
22064                     var last = this.last;
22065                     this.selectRange(this.last,  this.lastActive-1);
22066                     this.grid.getView().focusRow(this.lastActive);
22067                     if(last !== false){
22068                         this.last = last;
22069                     }
22070                 }else{
22071                     this.selectFirstRow();
22072                 }
22073                 this.fireEvent("afterselectionchange", this);
22074             },
22075             "down" : function(e){
22076                 if(!e.shiftKey){
22077                     this.selectNext(e.shiftKey);
22078                 }else if(this.last !== false && this.lastActive !== false){
22079                     var last = this.last;
22080                     this.selectRange(this.last,  this.lastActive+1);
22081                     this.grid.getView().focusRow(this.lastActive);
22082                     if(last !== false){
22083                         this.last = last;
22084                     }
22085                 }else{
22086                     this.selectFirstRow();
22087                 }
22088                 this.fireEvent("afterselectionchange", this);
22089             },
22090             scope: this
22091         });
22092
22093         var view = this.grid.view;
22094         view.on("refresh", this.onRefresh, this);
22095         view.on("rowupdated", this.onRowUpdated, this);
22096         view.on("rowremoved", this.onRemove, this);
22097     },
22098
22099     // private
22100     onRefresh : function(){
22101         var ds = this.grid.dataSource, i, v = this.grid.view;
22102         var s = this.selections;
22103         s.each(function(r){
22104             if((i = ds.indexOfId(r.id)) != -1){
22105                 v.onRowSelect(i);
22106             }else{
22107                 s.remove(r);
22108             }
22109         });
22110     },
22111
22112     // private
22113     onRemove : function(v, index, r){
22114         this.selections.remove(r);
22115     },
22116
22117     // private
22118     onRowUpdated : function(v, index, r){
22119         if(this.isSelected(r)){
22120             v.onRowSelect(index);
22121         }
22122     },
22123
22124     /**
22125      * Select records.
22126      * @param {Array} records The records to select
22127      * @param {Boolean} keepExisting (optional) True to keep existing selections
22128      */
22129     selectRecords : function(records, keepExisting){
22130         if(!keepExisting){
22131             this.clearSelections();
22132         }
22133         var ds = this.grid.dataSource;
22134         for(var i = 0, len = records.length; i < len; i++){
22135             this.selectRow(ds.indexOf(records[i]), true);
22136         }
22137     },
22138
22139     /**
22140      * Gets the number of selected rows.
22141      * @return {Number}
22142      */
22143     getCount : function(){
22144         return this.selections.length;
22145     },
22146
22147     /**
22148      * Selects the first row in the grid.
22149      */
22150     selectFirstRow : function(){
22151         this.selectRow(0);
22152     },
22153
22154     /**
22155      * Select the last row.
22156      * @param {Boolean} keepExisting (optional) True to keep existing selections
22157      */
22158     selectLastRow : function(keepExisting){
22159         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22160     },
22161
22162     /**
22163      * Selects the row immediately following the last selected row.
22164      * @param {Boolean} keepExisting (optional) True to keep existing selections
22165      */
22166     selectNext : function(keepExisting){
22167         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22168             this.selectRow(this.last+1, keepExisting);
22169             this.grid.getView().focusRow(this.last);
22170         }
22171     },
22172
22173     /**
22174      * Selects the row that precedes the last selected row.
22175      * @param {Boolean} keepExisting (optional) True to keep existing selections
22176      */
22177     selectPrevious : function(keepExisting){
22178         if(this.last){
22179             this.selectRow(this.last-1, keepExisting);
22180             this.grid.getView().focusRow(this.last);
22181         }
22182     },
22183
22184     /**
22185      * Returns the selected records
22186      * @return {Array} Array of selected records
22187      */
22188     getSelections : function(){
22189         return [].concat(this.selections.items);
22190     },
22191
22192     /**
22193      * Returns the first selected record.
22194      * @return {Record}
22195      */
22196     getSelected : function(){
22197         return this.selections.itemAt(0);
22198     },
22199
22200
22201     /**
22202      * Clears all selections.
22203      */
22204     clearSelections : function(fast){
22205         if(this.locked) {
22206             return;
22207         }
22208         if(fast !== true){
22209             var ds = this.grid.dataSource;
22210             var s = this.selections;
22211             s.each(function(r){
22212                 this.deselectRow(ds.indexOfId(r.id));
22213             }, this);
22214             s.clear();
22215         }else{
22216             this.selections.clear();
22217         }
22218         this.last = false;
22219     },
22220
22221
22222     /**
22223      * Selects all rows.
22224      */
22225     selectAll : function(){
22226         if(this.locked) {
22227             return;
22228         }
22229         this.selections.clear();
22230         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22231             this.selectRow(i, true);
22232         }
22233     },
22234
22235     /**
22236      * Returns True if there is a selection.
22237      * @return {Boolean}
22238      */
22239     hasSelection : function(){
22240         return this.selections.length > 0;
22241     },
22242
22243     /**
22244      * Returns True if the specified row is selected.
22245      * @param {Number/Record} record The record or index of the record to check
22246      * @return {Boolean}
22247      */
22248     isSelected : function(index){
22249         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22250         return (r && this.selections.key(r.id) ? true : false);
22251     },
22252
22253     /**
22254      * Returns True if the specified record id is selected.
22255      * @param {String} id The id of record to check
22256      * @return {Boolean}
22257      */
22258     isIdSelected : function(id){
22259         return (this.selections.key(id) ? true : false);
22260     },
22261
22262     // private
22263     handleMouseDown : function(e, t){
22264         var view = this.grid.getView(), rowIndex;
22265         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22266             return;
22267         };
22268         if(e.shiftKey && this.last !== false){
22269             var last = this.last;
22270             this.selectRange(last, rowIndex, e.ctrlKey);
22271             this.last = last; // reset the last
22272             view.focusRow(rowIndex);
22273         }else{
22274             var isSelected = this.isSelected(rowIndex);
22275             if(e.button !== 0 && isSelected){
22276                 view.focusRow(rowIndex);
22277             }else if(e.ctrlKey && isSelected){
22278                 this.deselectRow(rowIndex);
22279             }else if(!isSelected){
22280                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22281                 view.focusRow(rowIndex);
22282             }
22283         }
22284         this.fireEvent("afterselectionchange", this);
22285     },
22286     // private
22287     handleDragableRowClick :  function(grid, rowIndex, e) 
22288     {
22289         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22290             this.selectRow(rowIndex, false);
22291             grid.view.focusRow(rowIndex);
22292              this.fireEvent("afterselectionchange", this);
22293         }
22294     },
22295     
22296     /**
22297      * Selects multiple rows.
22298      * @param {Array} rows Array of the indexes of the row to select
22299      * @param {Boolean} keepExisting (optional) True to keep existing selections
22300      */
22301     selectRows : function(rows, keepExisting){
22302         if(!keepExisting){
22303             this.clearSelections();
22304         }
22305         for(var i = 0, len = rows.length; i < len; i++){
22306             this.selectRow(rows[i], true);
22307         }
22308     },
22309
22310     /**
22311      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22312      * @param {Number} startRow The index of the first row in the range
22313      * @param {Number} endRow The index of the last row in the range
22314      * @param {Boolean} keepExisting (optional) True to retain existing selections
22315      */
22316     selectRange : function(startRow, endRow, keepExisting){
22317         if(this.locked) {
22318             return;
22319         }
22320         if(!keepExisting){
22321             this.clearSelections();
22322         }
22323         if(startRow <= endRow){
22324             for(var i = startRow; i <= endRow; i++){
22325                 this.selectRow(i, true);
22326             }
22327         }else{
22328             for(var i = startRow; i >= endRow; i--){
22329                 this.selectRow(i, true);
22330             }
22331         }
22332     },
22333
22334     /**
22335      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22336      * @param {Number} startRow The index of the first row in the range
22337      * @param {Number} endRow The index of the last row in the range
22338      */
22339     deselectRange : function(startRow, endRow, preventViewNotify){
22340         if(this.locked) {
22341             return;
22342         }
22343         for(var i = startRow; i <= endRow; i++){
22344             this.deselectRow(i, preventViewNotify);
22345         }
22346     },
22347
22348     /**
22349      * Selects a row.
22350      * @param {Number} row The index of the row to select
22351      * @param {Boolean} keepExisting (optional) True to keep existing selections
22352      */
22353     selectRow : function(index, keepExisting, preventViewNotify){
22354         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22355             return;
22356         }
22357         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22358             if(!keepExisting || this.singleSelect){
22359                 this.clearSelections();
22360             }
22361             var r = this.grid.dataSource.getAt(index);
22362             this.selections.add(r);
22363             this.last = this.lastActive = index;
22364             if(!preventViewNotify){
22365                 this.grid.getView().onRowSelect(index);
22366             }
22367             this.fireEvent("rowselect", this, index, r);
22368             this.fireEvent("selectionchange", this);
22369         }
22370     },
22371
22372     /**
22373      * Deselects a row.
22374      * @param {Number} row The index of the row to deselect
22375      */
22376     deselectRow : function(index, preventViewNotify){
22377         if(this.locked) {
22378             return;
22379         }
22380         if(this.last == index){
22381             this.last = false;
22382         }
22383         if(this.lastActive == index){
22384             this.lastActive = false;
22385         }
22386         var r = this.grid.dataSource.getAt(index);
22387         this.selections.remove(r);
22388         if(!preventViewNotify){
22389             this.grid.getView().onRowDeselect(index);
22390         }
22391         this.fireEvent("rowdeselect", this, index);
22392         this.fireEvent("selectionchange", this);
22393     },
22394
22395     // private
22396     restoreLast : function(){
22397         if(this._last){
22398             this.last = this._last;
22399         }
22400     },
22401
22402     // private
22403     acceptsNav : function(row, col, cm){
22404         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22405     },
22406
22407     // private
22408     onEditorKey : function(field, e){
22409         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22410         if(k == e.TAB){
22411             e.stopEvent();
22412             ed.completeEdit();
22413             if(e.shiftKey){
22414                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22415             }else{
22416                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22417             }
22418         }else if(k == e.ENTER && !e.ctrlKey){
22419             e.stopEvent();
22420             ed.completeEdit();
22421             if(e.shiftKey){
22422                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22423             }else{
22424                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22425             }
22426         }else if(k == e.ESC){
22427             ed.cancelEdit();
22428         }
22429         if(newCell){
22430             g.startEditing(newCell[0], newCell[1]);
22431         }
22432     }
22433 });/*
22434  * Based on:
22435  * Ext JS Library 1.1.1
22436  * Copyright(c) 2006-2007, Ext JS, LLC.
22437  *
22438  * Originally Released Under LGPL - original licence link has changed is not relivant.
22439  *
22440  * Fork - LGPL
22441  * <script type="text/javascript">
22442  */
22443  
22444 /**
22445  * @class Roo.bootstrap.PagingToolbar
22446  * @extends Roo.bootstrap.NavSimplebar
22447  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22448  * @constructor
22449  * Create a new PagingToolbar
22450  * @param {Object} config The config object
22451  * @param {Roo.data.Store} store
22452  */
22453 Roo.bootstrap.PagingToolbar = function(config)
22454 {
22455     // old args format still supported... - xtype is prefered..
22456         // created from xtype...
22457     
22458     this.ds = config.dataSource;
22459     
22460     if (config.store && !this.ds) {
22461         this.store= Roo.factory(config.store, Roo.data);
22462         this.ds = this.store;
22463         this.ds.xmodule = this.xmodule || false;
22464     }
22465     
22466     this.toolbarItems = [];
22467     if (config.items) {
22468         this.toolbarItems = config.items;
22469     }
22470     
22471     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22472     
22473     this.cursor = 0;
22474     
22475     if (this.ds) { 
22476         this.bind(this.ds);
22477     }
22478     
22479     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22480     
22481 };
22482
22483 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22484     /**
22485      * @cfg {Roo.data.Store} dataSource
22486      * The underlying data store providing the paged data
22487      */
22488     /**
22489      * @cfg {String/HTMLElement/Element} container
22490      * container The id or element that will contain the toolbar
22491      */
22492     /**
22493      * @cfg {Boolean} displayInfo
22494      * True to display the displayMsg (defaults to false)
22495      */
22496     /**
22497      * @cfg {Number} pageSize
22498      * The number of records to display per page (defaults to 20)
22499      */
22500     pageSize: 20,
22501     /**
22502      * @cfg {String} displayMsg
22503      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22504      */
22505     displayMsg : 'Displaying {0} - {1} of {2}',
22506     /**
22507      * @cfg {String} emptyMsg
22508      * The message to display when no records are found (defaults to "No data to display")
22509      */
22510     emptyMsg : 'No data to display',
22511     /**
22512      * Customizable piece of the default paging text (defaults to "Page")
22513      * @type String
22514      */
22515     beforePageText : "Page",
22516     /**
22517      * Customizable piece of the default paging text (defaults to "of %0")
22518      * @type String
22519      */
22520     afterPageText : "of {0}",
22521     /**
22522      * Customizable piece of the default paging text (defaults to "First Page")
22523      * @type String
22524      */
22525     firstText : "First Page",
22526     /**
22527      * Customizable piece of the default paging text (defaults to "Previous Page")
22528      * @type String
22529      */
22530     prevText : "Previous Page",
22531     /**
22532      * Customizable piece of the default paging text (defaults to "Next Page")
22533      * @type String
22534      */
22535     nextText : "Next Page",
22536     /**
22537      * Customizable piece of the default paging text (defaults to "Last Page")
22538      * @type String
22539      */
22540     lastText : "Last Page",
22541     /**
22542      * Customizable piece of the default paging text (defaults to "Refresh")
22543      * @type String
22544      */
22545     refreshText : "Refresh",
22546
22547     buttons : false,
22548     // private
22549     onRender : function(ct, position) 
22550     {
22551         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22552         this.navgroup.parentId = this.id;
22553         this.navgroup.onRender(this.el, null);
22554         // add the buttons to the navgroup
22555         
22556         if(this.displayInfo){
22557             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22558             this.displayEl = this.el.select('.x-paging-info', true).first();
22559 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22560 //            this.displayEl = navel.el.select('span',true).first();
22561         }
22562         
22563         var _this = this;
22564         
22565         if(this.buttons){
22566             Roo.each(_this.buttons, function(e){ // this might need to use render????
22567                Roo.factory(e).onRender(_this.el, null);
22568             });
22569         }
22570             
22571         Roo.each(_this.toolbarItems, function(e) {
22572             _this.navgroup.addItem(e);
22573         });
22574         
22575         
22576         this.first = this.navgroup.addItem({
22577             tooltip: this.firstText,
22578             cls: "prev",
22579             icon : 'fa fa-backward',
22580             disabled: true,
22581             preventDefault: true,
22582             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22583         });
22584         
22585         this.prev =  this.navgroup.addItem({
22586             tooltip: this.prevText,
22587             cls: "prev",
22588             icon : 'fa fa-step-backward',
22589             disabled: true,
22590             preventDefault: true,
22591             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22592         });
22593     //this.addSeparator();
22594         
22595         
22596         var field = this.navgroup.addItem( {
22597             tagtype : 'span',
22598             cls : 'x-paging-position',
22599             
22600             html : this.beforePageText  +
22601                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22602                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22603          } ); //?? escaped?
22604         
22605         this.field = field.el.select('input', true).first();
22606         this.field.on("keydown", this.onPagingKeydown, this);
22607         this.field.on("focus", function(){this.dom.select();});
22608     
22609     
22610         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22611         //this.field.setHeight(18);
22612         //this.addSeparator();
22613         this.next = this.navgroup.addItem({
22614             tooltip: this.nextText,
22615             cls: "next",
22616             html : ' <i class="fa fa-step-forward">',
22617             disabled: true,
22618             preventDefault: true,
22619             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22620         });
22621         this.last = this.navgroup.addItem({
22622             tooltip: this.lastText,
22623             icon : 'fa fa-forward',
22624             cls: "next",
22625             disabled: true,
22626             preventDefault: true,
22627             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22628         });
22629     //this.addSeparator();
22630         this.loading = this.navgroup.addItem({
22631             tooltip: this.refreshText,
22632             icon: 'fa fa-refresh',
22633             preventDefault: true,
22634             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22635         });
22636         
22637     },
22638
22639     // private
22640     updateInfo : function(){
22641         if(this.displayEl){
22642             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22643             var msg = count == 0 ?
22644                 this.emptyMsg :
22645                 String.format(
22646                     this.displayMsg,
22647                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22648                 );
22649             this.displayEl.update(msg);
22650         }
22651     },
22652
22653     // private
22654     onLoad : function(ds, r, o){
22655        this.cursor = o.params ? o.params.start : 0;
22656        var d = this.getPageData(),
22657             ap = d.activePage,
22658             ps = d.pages;
22659         
22660        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22661        this.field.dom.value = ap;
22662        this.first.setDisabled(ap == 1);
22663        this.prev.setDisabled(ap == 1);
22664        this.next.setDisabled(ap == ps);
22665        this.last.setDisabled(ap == ps);
22666        this.loading.enable();
22667        this.updateInfo();
22668     },
22669
22670     // private
22671     getPageData : function(){
22672         var total = this.ds.getTotalCount();
22673         return {
22674             total : total,
22675             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22676             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22677         };
22678     },
22679
22680     // private
22681     onLoadError : function(){
22682         this.loading.enable();
22683     },
22684
22685     // private
22686     onPagingKeydown : function(e){
22687         var k = e.getKey();
22688         var d = this.getPageData();
22689         if(k == e.RETURN){
22690             var v = this.field.dom.value, pageNum;
22691             if(!v || isNaN(pageNum = parseInt(v, 10))){
22692                 this.field.dom.value = d.activePage;
22693                 return;
22694             }
22695             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22696             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22697             e.stopEvent();
22698         }
22699         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))
22700         {
22701           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22702           this.field.dom.value = pageNum;
22703           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22704           e.stopEvent();
22705         }
22706         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22707         {
22708           var v = this.field.dom.value, pageNum; 
22709           var increment = (e.shiftKey) ? 10 : 1;
22710           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22711                 increment *= -1;
22712           }
22713           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22714             this.field.dom.value = d.activePage;
22715             return;
22716           }
22717           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22718           {
22719             this.field.dom.value = parseInt(v, 10) + increment;
22720             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22721             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22722           }
22723           e.stopEvent();
22724         }
22725     },
22726
22727     // private
22728     beforeLoad : function(){
22729         if(this.loading){
22730             this.loading.disable();
22731         }
22732     },
22733
22734     // private
22735     onClick : function(which){
22736         
22737         var ds = this.ds;
22738         if (!ds) {
22739             return;
22740         }
22741         
22742         switch(which){
22743             case "first":
22744                 ds.load({params:{start: 0, limit: this.pageSize}});
22745             break;
22746             case "prev":
22747                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22748             break;
22749             case "next":
22750                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22751             break;
22752             case "last":
22753                 var total = ds.getTotalCount();
22754                 var extra = total % this.pageSize;
22755                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22756                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22757             break;
22758             case "refresh":
22759                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22760             break;
22761         }
22762     },
22763
22764     /**
22765      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22766      * @param {Roo.data.Store} store The data store to unbind
22767      */
22768     unbind : function(ds){
22769         ds.un("beforeload", this.beforeLoad, this);
22770         ds.un("load", this.onLoad, this);
22771         ds.un("loadexception", this.onLoadError, this);
22772         ds.un("remove", this.updateInfo, this);
22773         ds.un("add", this.updateInfo, this);
22774         this.ds = undefined;
22775     },
22776
22777     /**
22778      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22779      * @param {Roo.data.Store} store The data store to bind
22780      */
22781     bind : function(ds){
22782         ds.on("beforeload", this.beforeLoad, this);
22783         ds.on("load", this.onLoad, this);
22784         ds.on("loadexception", this.onLoadError, this);
22785         ds.on("remove", this.updateInfo, this);
22786         ds.on("add", this.updateInfo, this);
22787         this.ds = ds;
22788     }
22789 });/*
22790  * - LGPL
22791  *
22792  * element
22793  * 
22794  */
22795
22796 /**
22797  * @class Roo.bootstrap.MessageBar
22798  * @extends Roo.bootstrap.Component
22799  * Bootstrap MessageBar class
22800  * @cfg {String} html contents of the MessageBar
22801  * @cfg {String} weight (info | success | warning | danger) default info
22802  * @cfg {String} beforeClass insert the bar before the given class
22803  * @cfg {Boolean} closable (true | false) default false
22804  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22805  * 
22806  * @constructor
22807  * Create a new Element
22808  * @param {Object} config The config object
22809  */
22810
22811 Roo.bootstrap.MessageBar = function(config){
22812     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22813 };
22814
22815 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22816     
22817     html: '',
22818     weight: 'info',
22819     closable: false,
22820     fixed: false,
22821     beforeClass: 'bootstrap-sticky-wrap',
22822     
22823     getAutoCreate : function(){
22824         
22825         var cfg = {
22826             tag: 'div',
22827             cls: 'alert alert-dismissable alert-' + this.weight,
22828             cn: [
22829                 {
22830                     tag: 'span',
22831                     cls: 'message',
22832                     html: this.html || ''
22833                 }
22834             ]
22835         };
22836         
22837         if(this.fixed){
22838             cfg.cls += ' alert-messages-fixed';
22839         }
22840         
22841         if(this.closable){
22842             cfg.cn.push({
22843                 tag: 'button',
22844                 cls: 'close',
22845                 html: 'x'
22846             });
22847         }
22848         
22849         return cfg;
22850     },
22851     
22852     onRender : function(ct, position)
22853     {
22854         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22855         
22856         if(!this.el){
22857             var cfg = Roo.apply({},  this.getAutoCreate());
22858             cfg.id = Roo.id();
22859             
22860             if (this.cls) {
22861                 cfg.cls += ' ' + this.cls;
22862             }
22863             if (this.style) {
22864                 cfg.style = this.style;
22865             }
22866             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22867             
22868             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22869         }
22870         
22871         this.el.select('>button.close').on('click', this.hide, this);
22872         
22873     },
22874     
22875     show : function()
22876     {
22877         if (!this.rendered) {
22878             this.render();
22879         }
22880         
22881         this.el.show();
22882         
22883         this.fireEvent('show', this);
22884         
22885     },
22886     
22887     hide : function()
22888     {
22889         if (!this.rendered) {
22890             this.render();
22891         }
22892         
22893         this.el.hide();
22894         
22895         this.fireEvent('hide', this);
22896     },
22897     
22898     update : function()
22899     {
22900 //        var e = this.el.dom.firstChild;
22901 //        
22902 //        if(this.closable){
22903 //            e = e.nextSibling;
22904 //        }
22905 //        
22906 //        e.data = this.html || '';
22907
22908         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22909     }
22910    
22911 });
22912
22913  
22914
22915      /*
22916  * - LGPL
22917  *
22918  * Graph
22919  * 
22920  */
22921
22922
22923 /**
22924  * @class Roo.bootstrap.Graph
22925  * @extends Roo.bootstrap.Component
22926  * Bootstrap Graph class
22927 > Prameters
22928  -sm {number} sm 4
22929  -md {number} md 5
22930  @cfg {String} graphtype  bar | vbar | pie
22931  @cfg {number} g_x coodinator | centre x (pie)
22932  @cfg {number} g_y coodinator | centre y (pie)
22933  @cfg {number} g_r radius (pie)
22934  @cfg {number} g_height height of the chart (respected by all elements in the set)
22935  @cfg {number} g_width width of the chart (respected by all elements in the set)
22936  @cfg {Object} title The title of the chart
22937     
22938  -{Array}  values
22939  -opts (object) options for the chart 
22940      o {
22941      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22942      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22943      o vgutter (number)
22944      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.
22945      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22946      o to
22947      o stretch (boolean)
22948      o }
22949  -opts (object) options for the pie
22950      o{
22951      o cut
22952      o startAngle (number)
22953      o endAngle (number)
22954      } 
22955  *
22956  * @constructor
22957  * Create a new Input
22958  * @param {Object} config The config object
22959  */
22960
22961 Roo.bootstrap.Graph = function(config){
22962     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22963     
22964     this.addEvents({
22965         // img events
22966         /**
22967          * @event click
22968          * The img click event for the img.
22969          * @param {Roo.EventObject} e
22970          */
22971         "click" : true
22972     });
22973 };
22974
22975 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22976     
22977     sm: 4,
22978     md: 5,
22979     graphtype: 'bar',
22980     g_height: 250,
22981     g_width: 400,
22982     g_x: 50,
22983     g_y: 50,
22984     g_r: 30,
22985     opts:{
22986         //g_colors: this.colors,
22987         g_type: 'soft',
22988         g_gutter: '20%'
22989
22990     },
22991     title : false,
22992
22993     getAutoCreate : function(){
22994         
22995         var cfg = {
22996             tag: 'div',
22997             html : null
22998         };
22999         
23000         
23001         return  cfg;
23002     },
23003
23004     onRender : function(ct,position){
23005         
23006         
23007         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23008         
23009         if (typeof(Raphael) == 'undefined') {
23010             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23011             return;
23012         }
23013         
23014         this.raphael = Raphael(this.el.dom);
23015         
23016                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23017                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23018                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23019                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23020                 /*
23021                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23022                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23023                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23024                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23025                 
23026                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23027                 r.barchart(330, 10, 300, 220, data1);
23028                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23029                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23030                 */
23031                 
23032                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23033                 // r.barchart(30, 30, 560, 250,  xdata, {
23034                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23035                 //     axis : "0 0 1 1",
23036                 //     axisxlabels :  xdata
23037                 //     //yvalues : cols,
23038                    
23039                 // });
23040 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23041 //        
23042 //        this.load(null,xdata,{
23043 //                axis : "0 0 1 1",
23044 //                axisxlabels :  xdata
23045 //                });
23046
23047     },
23048
23049     load : function(graphtype,xdata,opts)
23050     {
23051         this.raphael.clear();
23052         if(!graphtype) {
23053             graphtype = this.graphtype;
23054         }
23055         if(!opts){
23056             opts = this.opts;
23057         }
23058         var r = this.raphael,
23059             fin = function () {
23060                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23061             },
23062             fout = function () {
23063                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23064             },
23065             pfin = function() {
23066                 this.sector.stop();
23067                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23068
23069                 if (this.label) {
23070                     this.label[0].stop();
23071                     this.label[0].attr({ r: 7.5 });
23072                     this.label[1].attr({ "font-weight": 800 });
23073                 }
23074             },
23075             pfout = function() {
23076                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23077
23078                 if (this.label) {
23079                     this.label[0].animate({ r: 5 }, 500, "bounce");
23080                     this.label[1].attr({ "font-weight": 400 });
23081                 }
23082             };
23083
23084         switch(graphtype){
23085             case 'bar':
23086                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23087                 break;
23088             case 'hbar':
23089                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23090                 break;
23091             case 'pie':
23092 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23093 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23094 //            
23095                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23096                 
23097                 break;
23098
23099         }
23100         
23101         if(this.title){
23102             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23103         }
23104         
23105     },
23106     
23107     setTitle: function(o)
23108     {
23109         this.title = o;
23110     },
23111     
23112     initEvents: function() {
23113         
23114         if(!this.href){
23115             this.el.on('click', this.onClick, this);
23116         }
23117     },
23118     
23119     onClick : function(e)
23120     {
23121         Roo.log('img onclick');
23122         this.fireEvent('click', this, e);
23123     }
23124    
23125 });
23126
23127  
23128 /*
23129  * - LGPL
23130  *
23131  * numberBox
23132  * 
23133  */
23134 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23135
23136 /**
23137  * @class Roo.bootstrap.dash.NumberBox
23138  * @extends Roo.bootstrap.Component
23139  * Bootstrap NumberBox class
23140  * @cfg {String} headline Box headline
23141  * @cfg {String} content Box content
23142  * @cfg {String} icon Box icon
23143  * @cfg {String} footer Footer text
23144  * @cfg {String} fhref Footer href
23145  * 
23146  * @constructor
23147  * Create a new NumberBox
23148  * @param {Object} config The config object
23149  */
23150
23151
23152 Roo.bootstrap.dash.NumberBox = function(config){
23153     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23154     
23155 };
23156
23157 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23158     
23159     headline : '',
23160     content : '',
23161     icon : '',
23162     footer : '',
23163     fhref : '',
23164     ficon : '',
23165     
23166     getAutoCreate : function(){
23167         
23168         var cfg = {
23169             tag : 'div',
23170             cls : 'small-box ',
23171             cn : [
23172                 {
23173                     tag : 'div',
23174                     cls : 'inner',
23175                     cn :[
23176                         {
23177                             tag : 'h3',
23178                             cls : 'roo-headline',
23179                             html : this.headline
23180                         },
23181                         {
23182                             tag : 'p',
23183                             cls : 'roo-content',
23184                             html : this.content
23185                         }
23186                     ]
23187                 }
23188             ]
23189         };
23190         
23191         if(this.icon){
23192             cfg.cn.push({
23193                 tag : 'div',
23194                 cls : 'icon',
23195                 cn :[
23196                     {
23197                         tag : 'i',
23198                         cls : 'ion ' + this.icon
23199                     }
23200                 ]
23201             });
23202         }
23203         
23204         if(this.footer){
23205             var footer = {
23206                 tag : 'a',
23207                 cls : 'small-box-footer',
23208                 href : this.fhref || '#',
23209                 html : this.footer
23210             };
23211             
23212             cfg.cn.push(footer);
23213             
23214         }
23215         
23216         return  cfg;
23217     },
23218
23219     onRender : function(ct,position){
23220         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23221
23222
23223        
23224                 
23225     },
23226
23227     setHeadline: function (value)
23228     {
23229         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23230     },
23231     
23232     setFooter: function (value, href)
23233     {
23234         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23235         
23236         if(href){
23237             this.el.select('a.small-box-footer',true).first().attr('href', href);
23238         }
23239         
23240     },
23241
23242     setContent: function (value)
23243     {
23244         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23245     },
23246
23247     initEvents: function() 
23248     {   
23249         
23250     }
23251     
23252 });
23253
23254  
23255 /*
23256  * - LGPL
23257  *
23258  * TabBox
23259  * 
23260  */
23261 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23262
23263 /**
23264  * @class Roo.bootstrap.dash.TabBox
23265  * @extends Roo.bootstrap.Component
23266  * Bootstrap TabBox class
23267  * @cfg {String} title Title of the TabBox
23268  * @cfg {String} icon Icon of the TabBox
23269  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23270  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23271  * 
23272  * @constructor
23273  * Create a new TabBox
23274  * @param {Object} config The config object
23275  */
23276
23277
23278 Roo.bootstrap.dash.TabBox = function(config){
23279     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23280     this.addEvents({
23281         // raw events
23282         /**
23283          * @event addpane
23284          * When a pane is added
23285          * @param {Roo.bootstrap.dash.TabPane} pane
23286          */
23287         "addpane" : true,
23288         /**
23289          * @event activatepane
23290          * When a pane is activated
23291          * @param {Roo.bootstrap.dash.TabPane} pane
23292          */
23293         "activatepane" : true
23294         
23295          
23296     });
23297     
23298     this.panes = [];
23299 };
23300
23301 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23302
23303     title : '',
23304     icon : false,
23305     showtabs : true,
23306     tabScrollable : false,
23307     
23308     getChildContainer : function()
23309     {
23310         return this.el.select('.tab-content', true).first();
23311     },
23312     
23313     getAutoCreate : function(){
23314         
23315         var header = {
23316             tag: 'li',
23317             cls: 'pull-left header',
23318             html: this.title,
23319             cn : []
23320         };
23321         
23322         if(this.icon){
23323             header.cn.push({
23324                 tag: 'i',
23325                 cls: 'fa ' + this.icon
23326             });
23327         }
23328         
23329         var h = {
23330             tag: 'ul',
23331             cls: 'nav nav-tabs pull-right',
23332             cn: [
23333                 header
23334             ]
23335         };
23336         
23337         if(this.tabScrollable){
23338             h = {
23339                 tag: 'div',
23340                 cls: 'tab-header',
23341                 cn: [
23342                     {
23343                         tag: 'ul',
23344                         cls: 'nav nav-tabs pull-right',
23345                         cn: [
23346                             header
23347                         ]
23348                     }
23349                 ]
23350             };
23351         }
23352         
23353         var cfg = {
23354             tag: 'div',
23355             cls: 'nav-tabs-custom',
23356             cn: [
23357                 h,
23358                 {
23359                     tag: 'div',
23360                     cls: 'tab-content no-padding',
23361                     cn: []
23362                 }
23363             ]
23364         };
23365
23366         return  cfg;
23367     },
23368     initEvents : function()
23369     {
23370         //Roo.log('add add pane handler');
23371         this.on('addpane', this.onAddPane, this);
23372     },
23373      /**
23374      * Updates the box title
23375      * @param {String} html to set the title to.
23376      */
23377     setTitle : function(value)
23378     {
23379         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23380     },
23381     onAddPane : function(pane)
23382     {
23383         this.panes.push(pane);
23384         //Roo.log('addpane');
23385         //Roo.log(pane);
23386         // tabs are rendere left to right..
23387         if(!this.showtabs){
23388             return;
23389         }
23390         
23391         var ctr = this.el.select('.nav-tabs', true).first();
23392          
23393          
23394         var existing = ctr.select('.nav-tab',true);
23395         var qty = existing.getCount();;
23396         
23397         
23398         var tab = ctr.createChild({
23399             tag : 'li',
23400             cls : 'nav-tab' + (qty ? '' : ' active'),
23401             cn : [
23402                 {
23403                     tag : 'a',
23404                     href:'#',
23405                     html : pane.title
23406                 }
23407             ]
23408         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23409         pane.tab = tab;
23410         
23411         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23412         if (!qty) {
23413             pane.el.addClass('active');
23414         }
23415         
23416                 
23417     },
23418     onTabClick : function(ev,un,ob,pane)
23419     {
23420         //Roo.log('tab - prev default');
23421         ev.preventDefault();
23422         
23423         
23424         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23425         pane.tab.addClass('active');
23426         //Roo.log(pane.title);
23427         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23428         // technically we should have a deactivate event.. but maybe add later.
23429         // and it should not de-activate the selected tab...
23430         this.fireEvent('activatepane', pane);
23431         pane.el.addClass('active');
23432         pane.fireEvent('activate');
23433         
23434         
23435     },
23436     
23437     getActivePane : function()
23438     {
23439         var r = false;
23440         Roo.each(this.panes, function(p) {
23441             if(p.el.hasClass('active')){
23442                 r = p;
23443                 return false;
23444             }
23445             
23446             return;
23447         });
23448         
23449         return r;
23450     }
23451     
23452     
23453 });
23454
23455  
23456 /*
23457  * - LGPL
23458  *
23459  * Tab pane
23460  * 
23461  */
23462 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23463 /**
23464  * @class Roo.bootstrap.TabPane
23465  * @extends Roo.bootstrap.Component
23466  * Bootstrap TabPane class
23467  * @cfg {Boolean} active (false | true) Default false
23468  * @cfg {String} title title of panel
23469
23470  * 
23471  * @constructor
23472  * Create a new TabPane
23473  * @param {Object} config The config object
23474  */
23475
23476 Roo.bootstrap.dash.TabPane = function(config){
23477     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23478     
23479     this.addEvents({
23480         // raw events
23481         /**
23482          * @event activate
23483          * When a pane is activated
23484          * @param {Roo.bootstrap.dash.TabPane} pane
23485          */
23486         "activate" : true
23487          
23488     });
23489 };
23490
23491 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23492     
23493     active : false,
23494     title : '',
23495     
23496     // the tabBox that this is attached to.
23497     tab : false,
23498      
23499     getAutoCreate : function() 
23500     {
23501         var cfg = {
23502             tag: 'div',
23503             cls: 'tab-pane'
23504         };
23505         
23506         if(this.active){
23507             cfg.cls += ' active';
23508         }
23509         
23510         return cfg;
23511     },
23512     initEvents  : function()
23513     {
23514         //Roo.log('trigger add pane handler');
23515         this.parent().fireEvent('addpane', this)
23516     },
23517     
23518      /**
23519      * Updates the tab title 
23520      * @param {String} html to set the title to.
23521      */
23522     setTitle: function(str)
23523     {
23524         if (!this.tab) {
23525             return;
23526         }
23527         this.title = str;
23528         this.tab.select('a', true).first().dom.innerHTML = str;
23529         
23530     }
23531     
23532     
23533     
23534 });
23535
23536  
23537
23538
23539  /*
23540  * - LGPL
23541  *
23542  * menu
23543  * 
23544  */
23545 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23546
23547 /**
23548  * @class Roo.bootstrap.menu.Menu
23549  * @extends Roo.bootstrap.Component
23550  * Bootstrap Menu class - container for Menu
23551  * @cfg {String} html Text of the menu
23552  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23553  * @cfg {String} icon Font awesome icon
23554  * @cfg {String} pos Menu align to (top | bottom) default bottom
23555  * 
23556  * 
23557  * @constructor
23558  * Create a new Menu
23559  * @param {Object} config The config object
23560  */
23561
23562
23563 Roo.bootstrap.menu.Menu = function(config){
23564     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23565     
23566     this.addEvents({
23567         /**
23568          * @event beforeshow
23569          * Fires before this menu is displayed
23570          * @param {Roo.bootstrap.menu.Menu} this
23571          */
23572         beforeshow : true,
23573         /**
23574          * @event beforehide
23575          * Fires before this menu is hidden
23576          * @param {Roo.bootstrap.menu.Menu} this
23577          */
23578         beforehide : true,
23579         /**
23580          * @event show
23581          * Fires after this menu is displayed
23582          * @param {Roo.bootstrap.menu.Menu} this
23583          */
23584         show : true,
23585         /**
23586          * @event hide
23587          * Fires after this menu is hidden
23588          * @param {Roo.bootstrap.menu.Menu} this
23589          */
23590         hide : true,
23591         /**
23592          * @event click
23593          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23594          * @param {Roo.bootstrap.menu.Menu} this
23595          * @param {Roo.EventObject} e
23596          */
23597         click : true
23598     });
23599     
23600 };
23601
23602 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23603     
23604     submenu : false,
23605     html : '',
23606     weight : 'default',
23607     icon : false,
23608     pos : 'bottom',
23609     
23610     
23611     getChildContainer : function() {
23612         if(this.isSubMenu){
23613             return this.el;
23614         }
23615         
23616         return this.el.select('ul.dropdown-menu', true).first();  
23617     },
23618     
23619     getAutoCreate : function()
23620     {
23621         var text = [
23622             {
23623                 tag : 'span',
23624                 cls : 'roo-menu-text',
23625                 html : this.html
23626             }
23627         ];
23628         
23629         if(this.icon){
23630             text.unshift({
23631                 tag : 'i',
23632                 cls : 'fa ' + this.icon
23633             })
23634         }
23635         
23636         
23637         var cfg = {
23638             tag : 'div',
23639             cls : 'btn-group',
23640             cn : [
23641                 {
23642                     tag : 'button',
23643                     cls : 'dropdown-button btn btn-' + this.weight,
23644                     cn : text
23645                 },
23646                 {
23647                     tag : 'button',
23648                     cls : 'dropdown-toggle btn btn-' + this.weight,
23649                     cn : [
23650                         {
23651                             tag : 'span',
23652                             cls : 'caret'
23653                         }
23654                     ]
23655                 },
23656                 {
23657                     tag : 'ul',
23658                     cls : 'dropdown-menu'
23659                 }
23660             ]
23661             
23662         };
23663         
23664         if(this.pos == 'top'){
23665             cfg.cls += ' dropup';
23666         }
23667         
23668         if(this.isSubMenu){
23669             cfg = {
23670                 tag : 'ul',
23671                 cls : 'dropdown-menu'
23672             }
23673         }
23674         
23675         return cfg;
23676     },
23677     
23678     onRender : function(ct, position)
23679     {
23680         this.isSubMenu = ct.hasClass('dropdown-submenu');
23681         
23682         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23683     },
23684     
23685     initEvents : function() 
23686     {
23687         if(this.isSubMenu){
23688             return;
23689         }
23690         
23691         this.hidden = true;
23692         
23693         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23694         this.triggerEl.on('click', this.onTriggerPress, this);
23695         
23696         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23697         this.buttonEl.on('click', this.onClick, this);
23698         
23699     },
23700     
23701     list : function()
23702     {
23703         if(this.isSubMenu){
23704             return this.el;
23705         }
23706         
23707         return this.el.select('ul.dropdown-menu', true).first();
23708     },
23709     
23710     onClick : function(e)
23711     {
23712         this.fireEvent("click", this, e);
23713     },
23714     
23715     onTriggerPress  : function(e)
23716     {   
23717         if (this.isVisible()) {
23718             this.hide();
23719         } else {
23720             this.show();
23721         }
23722     },
23723     
23724     isVisible : function(){
23725         return !this.hidden;
23726     },
23727     
23728     show : function()
23729     {
23730         this.fireEvent("beforeshow", this);
23731         
23732         this.hidden = false;
23733         this.el.addClass('open');
23734         
23735         Roo.get(document).on("mouseup", this.onMouseUp, this);
23736         
23737         this.fireEvent("show", this);
23738         
23739         
23740     },
23741     
23742     hide : function()
23743     {
23744         this.fireEvent("beforehide", this);
23745         
23746         this.hidden = true;
23747         this.el.removeClass('open');
23748         
23749         Roo.get(document).un("mouseup", this.onMouseUp);
23750         
23751         this.fireEvent("hide", this);
23752     },
23753     
23754     onMouseUp : function()
23755     {
23756         this.hide();
23757     }
23758     
23759 });
23760
23761  
23762  /*
23763  * - LGPL
23764  *
23765  * menu item
23766  * 
23767  */
23768 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23769
23770 /**
23771  * @class Roo.bootstrap.menu.Item
23772  * @extends Roo.bootstrap.Component
23773  * Bootstrap MenuItem class
23774  * @cfg {Boolean} submenu (true | false) default false
23775  * @cfg {String} html text of the item
23776  * @cfg {String} href the link
23777  * @cfg {Boolean} disable (true | false) default false
23778  * @cfg {Boolean} preventDefault (true | false) default true
23779  * @cfg {String} icon Font awesome icon
23780  * @cfg {String} pos Submenu align to (left | right) default right 
23781  * 
23782  * 
23783  * @constructor
23784  * Create a new Item
23785  * @param {Object} config The config object
23786  */
23787
23788
23789 Roo.bootstrap.menu.Item = function(config){
23790     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23791     this.addEvents({
23792         /**
23793          * @event mouseover
23794          * Fires when the mouse is hovering over this menu
23795          * @param {Roo.bootstrap.menu.Item} this
23796          * @param {Roo.EventObject} e
23797          */
23798         mouseover : true,
23799         /**
23800          * @event mouseout
23801          * Fires when the mouse exits this menu
23802          * @param {Roo.bootstrap.menu.Item} this
23803          * @param {Roo.EventObject} e
23804          */
23805         mouseout : true,
23806         // raw events
23807         /**
23808          * @event click
23809          * The raw click event for the entire grid.
23810          * @param {Roo.EventObject} e
23811          */
23812         click : true
23813     });
23814 };
23815
23816 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23817     
23818     submenu : false,
23819     href : '',
23820     html : '',
23821     preventDefault: true,
23822     disable : false,
23823     icon : false,
23824     pos : 'right',
23825     
23826     getAutoCreate : function()
23827     {
23828         var text = [
23829             {
23830                 tag : 'span',
23831                 cls : 'roo-menu-item-text',
23832                 html : this.html
23833             }
23834         ];
23835         
23836         if(this.icon){
23837             text.unshift({
23838                 tag : 'i',
23839                 cls : 'fa ' + this.icon
23840             })
23841         }
23842         
23843         var cfg = {
23844             tag : 'li',
23845             cn : [
23846                 {
23847                     tag : 'a',
23848                     href : this.href || '#',
23849                     cn : text
23850                 }
23851             ]
23852         };
23853         
23854         if(this.disable){
23855             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23856         }
23857         
23858         if(this.submenu){
23859             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23860             
23861             if(this.pos == 'left'){
23862                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23863             }
23864         }
23865         
23866         return cfg;
23867     },
23868     
23869     initEvents : function() 
23870     {
23871         this.el.on('mouseover', this.onMouseOver, this);
23872         this.el.on('mouseout', this.onMouseOut, this);
23873         
23874         this.el.select('a', true).first().on('click', this.onClick, this);
23875         
23876     },
23877     
23878     onClick : function(e)
23879     {
23880         if(this.preventDefault){
23881             e.preventDefault();
23882         }
23883         
23884         this.fireEvent("click", this, e);
23885     },
23886     
23887     onMouseOver : function(e)
23888     {
23889         if(this.submenu && this.pos == 'left'){
23890             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23891         }
23892         
23893         this.fireEvent("mouseover", this, e);
23894     },
23895     
23896     onMouseOut : function(e)
23897     {
23898         this.fireEvent("mouseout", this, e);
23899     }
23900 });
23901
23902  
23903
23904  /*
23905  * - LGPL
23906  *
23907  * menu separator
23908  * 
23909  */
23910 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23911
23912 /**
23913  * @class Roo.bootstrap.menu.Separator
23914  * @extends Roo.bootstrap.Component
23915  * Bootstrap Separator class
23916  * 
23917  * @constructor
23918  * Create a new Separator
23919  * @param {Object} config The config object
23920  */
23921
23922
23923 Roo.bootstrap.menu.Separator = function(config){
23924     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23925 };
23926
23927 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23928     
23929     getAutoCreate : function(){
23930         var cfg = {
23931             tag : 'li',
23932             cls: 'divider'
23933         };
23934         
23935         return cfg;
23936     }
23937    
23938 });
23939
23940  
23941
23942  /*
23943  * - LGPL
23944  *
23945  * Tooltip
23946  * 
23947  */
23948
23949 /**
23950  * @class Roo.bootstrap.Tooltip
23951  * Bootstrap Tooltip class
23952  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23953  * to determine which dom element triggers the tooltip.
23954  * 
23955  * It needs to add support for additional attributes like tooltip-position
23956  * 
23957  * @constructor
23958  * Create a new Toolti
23959  * @param {Object} config The config object
23960  */
23961
23962 Roo.bootstrap.Tooltip = function(config){
23963     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23964 };
23965
23966 Roo.apply(Roo.bootstrap.Tooltip, {
23967     /**
23968      * @function init initialize tooltip monitoring.
23969      * @static
23970      */
23971     currentEl : false,
23972     currentTip : false,
23973     currentRegion : false,
23974     
23975     //  init : delay?
23976     
23977     init : function()
23978     {
23979         Roo.get(document).on('mouseover', this.enter ,this);
23980         Roo.get(document).on('mouseout', this.leave, this);
23981          
23982         
23983         this.currentTip = new Roo.bootstrap.Tooltip();
23984     },
23985     
23986     enter : function(ev)
23987     {
23988         var dom = ev.getTarget();
23989         
23990         //Roo.log(['enter',dom]);
23991         var el = Roo.fly(dom);
23992         if (this.currentEl) {
23993             //Roo.log(dom);
23994             //Roo.log(this.currentEl);
23995             //Roo.log(this.currentEl.contains(dom));
23996             if (this.currentEl == el) {
23997                 return;
23998             }
23999             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24000                 return;
24001             }
24002
24003         }
24004         
24005         if (this.currentTip.el) {
24006             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24007         }    
24008         //Roo.log(ev);
24009         var bindEl = el;
24010         
24011         // you can not look for children, as if el is the body.. then everythign is the child..
24012         if (!el.attr('tooltip')) { //
24013             if (!el.select("[tooltip]").elements.length) {
24014                 return;
24015             }
24016             // is the mouse over this child...?
24017             bindEl = el.select("[tooltip]").first();
24018             var xy = ev.getXY();
24019             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24020                 //Roo.log("not in region.");
24021                 return;
24022             }
24023             //Roo.log("child element over..");
24024             
24025         }
24026         this.currentEl = bindEl;
24027         this.currentTip.bind(bindEl);
24028         this.currentRegion = Roo.lib.Region.getRegion(dom);
24029         this.currentTip.enter();
24030         
24031     },
24032     leave : function(ev)
24033     {
24034         var dom = ev.getTarget();
24035         //Roo.log(['leave',dom]);
24036         if (!this.currentEl) {
24037             return;
24038         }
24039         
24040         
24041         if (dom != this.currentEl.dom) {
24042             return;
24043         }
24044         var xy = ev.getXY();
24045         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24046             return;
24047         }
24048         // only activate leave if mouse cursor is outside... bounding box..
24049         
24050         
24051         
24052         
24053         if (this.currentTip) {
24054             this.currentTip.leave();
24055         }
24056         //Roo.log('clear currentEl');
24057         this.currentEl = false;
24058         
24059         
24060     },
24061     alignment : {
24062         'left' : ['r-l', [-2,0], 'right'],
24063         'right' : ['l-r', [2,0], 'left'],
24064         'bottom' : ['t-b', [0,2], 'top'],
24065         'top' : [ 'b-t', [0,-2], 'bottom']
24066     }
24067     
24068 });
24069
24070
24071 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24072     
24073     
24074     bindEl : false,
24075     
24076     delay : null, // can be { show : 300 , hide: 500}
24077     
24078     timeout : null,
24079     
24080     hoverState : null, //???
24081     
24082     placement : 'bottom', 
24083     
24084     getAutoCreate : function(){
24085     
24086         var cfg = {
24087            cls : 'tooltip',
24088            role : 'tooltip',
24089            cn : [
24090                 {
24091                     cls : 'tooltip-arrow'
24092                 },
24093                 {
24094                     cls : 'tooltip-inner'
24095                 }
24096            ]
24097         };
24098         
24099         return cfg;
24100     },
24101     bind : function(el)
24102     {
24103         this.bindEl = el;
24104     },
24105       
24106     
24107     enter : function () {
24108        
24109         if (this.timeout != null) {
24110             clearTimeout(this.timeout);
24111         }
24112         
24113         this.hoverState = 'in';
24114          //Roo.log("enter - show");
24115         if (!this.delay || !this.delay.show) {
24116             this.show();
24117             return;
24118         }
24119         var _t = this;
24120         this.timeout = setTimeout(function () {
24121             if (_t.hoverState == 'in') {
24122                 _t.show();
24123             }
24124         }, this.delay.show);
24125     },
24126     leave : function()
24127     {
24128         clearTimeout(this.timeout);
24129     
24130         this.hoverState = 'out';
24131          if (!this.delay || !this.delay.hide) {
24132             this.hide();
24133             return;
24134         }
24135        
24136         var _t = this;
24137         this.timeout = setTimeout(function () {
24138             //Roo.log("leave - timeout");
24139             
24140             if (_t.hoverState == 'out') {
24141                 _t.hide();
24142                 Roo.bootstrap.Tooltip.currentEl = false;
24143             }
24144         }, delay);
24145     },
24146     
24147     show : function ()
24148     {
24149         if (!this.el) {
24150             this.render(document.body);
24151         }
24152         // set content.
24153         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24154         
24155         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24156         
24157         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24158         
24159         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24160         
24161         var placement = typeof this.placement == 'function' ?
24162             this.placement.call(this, this.el, on_el) :
24163             this.placement;
24164             
24165         var autoToken = /\s?auto?\s?/i;
24166         var autoPlace = autoToken.test(placement);
24167         if (autoPlace) {
24168             placement = placement.replace(autoToken, '') || 'top';
24169         }
24170         
24171         //this.el.detach()
24172         //this.el.setXY([0,0]);
24173         this.el.show();
24174         //this.el.dom.style.display='block';
24175         
24176         //this.el.appendTo(on_el);
24177         
24178         var p = this.getPosition();
24179         var box = this.el.getBox();
24180         
24181         if (autoPlace) {
24182             // fixme..
24183         }
24184         
24185         var align = Roo.bootstrap.Tooltip.alignment[placement];
24186         
24187         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24188         
24189         if(placement == 'top' || placement == 'bottom'){
24190             if(xy[0] < 0){
24191                 placement = 'right';
24192             }
24193             
24194             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24195                 placement = 'left';
24196             }
24197         }
24198         
24199         align = Roo.bootstrap.Tooltip.alignment[placement];
24200         
24201         this.el.alignTo(this.bindEl, align[0],align[1]);
24202         //var arrow = this.el.select('.arrow',true).first();
24203         //arrow.set(align[2], 
24204         
24205         this.el.addClass(placement);
24206         
24207         this.el.addClass('in fade');
24208         
24209         this.hoverState = null;
24210         
24211         if (this.el.hasClass('fade')) {
24212             // fade it?
24213         }
24214         
24215     },
24216     hide : function()
24217     {
24218          
24219         if (!this.el) {
24220             return;
24221         }
24222         //this.el.setXY([0,0]);
24223         this.el.removeClass('in');
24224         //this.el.hide();
24225         
24226     }
24227     
24228 });
24229  
24230
24231  /*
24232  * - LGPL
24233  *
24234  * Location Picker
24235  * 
24236  */
24237
24238 /**
24239  * @class Roo.bootstrap.LocationPicker
24240  * @extends Roo.bootstrap.Component
24241  * Bootstrap LocationPicker class
24242  * @cfg {Number} latitude Position when init default 0
24243  * @cfg {Number} longitude Position when init default 0
24244  * @cfg {Number} zoom default 15
24245  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24246  * @cfg {Boolean} mapTypeControl default false
24247  * @cfg {Boolean} disableDoubleClickZoom default false
24248  * @cfg {Boolean} scrollwheel default true
24249  * @cfg {Boolean} streetViewControl default false
24250  * @cfg {Number} radius default 0
24251  * @cfg {String} locationName
24252  * @cfg {Boolean} draggable default true
24253  * @cfg {Boolean} enableAutocomplete default false
24254  * @cfg {Boolean} enableReverseGeocode default true
24255  * @cfg {String} markerTitle
24256  * 
24257  * @constructor
24258  * Create a new LocationPicker
24259  * @param {Object} config The config object
24260  */
24261
24262
24263 Roo.bootstrap.LocationPicker = function(config){
24264     
24265     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24266     
24267     this.addEvents({
24268         /**
24269          * @event initial
24270          * Fires when the picker initialized.
24271          * @param {Roo.bootstrap.LocationPicker} this
24272          * @param {Google Location} location
24273          */
24274         initial : true,
24275         /**
24276          * @event positionchanged
24277          * Fires when the picker position changed.
24278          * @param {Roo.bootstrap.LocationPicker} this
24279          * @param {Google Location} location
24280          */
24281         positionchanged : true,
24282         /**
24283          * @event resize
24284          * Fires when the map resize.
24285          * @param {Roo.bootstrap.LocationPicker} this
24286          */
24287         resize : true,
24288         /**
24289          * @event show
24290          * Fires when the map show.
24291          * @param {Roo.bootstrap.LocationPicker} this
24292          */
24293         show : true,
24294         /**
24295          * @event hide
24296          * Fires when the map hide.
24297          * @param {Roo.bootstrap.LocationPicker} this
24298          */
24299         hide : true,
24300         /**
24301          * @event mapClick
24302          * Fires when click the map.
24303          * @param {Roo.bootstrap.LocationPicker} this
24304          * @param {Map event} e
24305          */
24306         mapClick : true,
24307         /**
24308          * @event mapRightClick
24309          * Fires when right click the map.
24310          * @param {Roo.bootstrap.LocationPicker} this
24311          * @param {Map event} e
24312          */
24313         mapRightClick : true,
24314         /**
24315          * @event markerClick
24316          * Fires when click the marker.
24317          * @param {Roo.bootstrap.LocationPicker} this
24318          * @param {Map event} e
24319          */
24320         markerClick : true,
24321         /**
24322          * @event markerRightClick
24323          * Fires when right click the marker.
24324          * @param {Roo.bootstrap.LocationPicker} this
24325          * @param {Map event} e
24326          */
24327         markerRightClick : true,
24328         /**
24329          * @event OverlayViewDraw
24330          * Fires when OverlayView Draw
24331          * @param {Roo.bootstrap.LocationPicker} this
24332          */
24333         OverlayViewDraw : true,
24334         /**
24335          * @event OverlayViewOnAdd
24336          * Fires when OverlayView Draw
24337          * @param {Roo.bootstrap.LocationPicker} this
24338          */
24339         OverlayViewOnAdd : true,
24340         /**
24341          * @event OverlayViewOnRemove
24342          * Fires when OverlayView Draw
24343          * @param {Roo.bootstrap.LocationPicker} this
24344          */
24345         OverlayViewOnRemove : true,
24346         /**
24347          * @event OverlayViewShow
24348          * Fires when OverlayView Draw
24349          * @param {Roo.bootstrap.LocationPicker} this
24350          * @param {Pixel} cpx
24351          */
24352         OverlayViewShow : true,
24353         /**
24354          * @event OverlayViewHide
24355          * Fires when OverlayView Draw
24356          * @param {Roo.bootstrap.LocationPicker} this
24357          */
24358         OverlayViewHide : true,
24359         /**
24360          * @event loadexception
24361          * Fires when load google lib failed.
24362          * @param {Roo.bootstrap.LocationPicker} this
24363          */
24364         loadexception : true
24365     });
24366         
24367 };
24368
24369 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24370     
24371     gMapContext: false,
24372     
24373     latitude: 0,
24374     longitude: 0,
24375     zoom: 15,
24376     mapTypeId: false,
24377     mapTypeControl: false,
24378     disableDoubleClickZoom: false,
24379     scrollwheel: true,
24380     streetViewControl: false,
24381     radius: 0,
24382     locationName: '',
24383     draggable: true,
24384     enableAutocomplete: false,
24385     enableReverseGeocode: true,
24386     markerTitle: '',
24387     
24388     getAutoCreate: function()
24389     {
24390
24391         var cfg = {
24392             tag: 'div',
24393             cls: 'roo-location-picker'
24394         };
24395         
24396         return cfg
24397     },
24398     
24399     initEvents: function(ct, position)
24400     {       
24401         if(!this.el.getWidth() || this.isApplied()){
24402             return;
24403         }
24404         
24405         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24406         
24407         this.initial();
24408     },
24409     
24410     initial: function()
24411     {
24412         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24413             this.fireEvent('loadexception', this);
24414             return;
24415         }
24416         
24417         if(!this.mapTypeId){
24418             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24419         }
24420         
24421         this.gMapContext = this.GMapContext();
24422         
24423         this.initOverlayView();
24424         
24425         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24426         
24427         var _this = this;
24428                 
24429         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24430             _this.setPosition(_this.gMapContext.marker.position);
24431         });
24432         
24433         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24434             _this.fireEvent('mapClick', this, event);
24435             
24436         });
24437
24438         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24439             _this.fireEvent('mapRightClick', this, event);
24440             
24441         });
24442         
24443         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24444             _this.fireEvent('markerClick', this, event);
24445             
24446         });
24447
24448         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24449             _this.fireEvent('markerRightClick', this, event);
24450             
24451         });
24452         
24453         this.setPosition(this.gMapContext.location);
24454         
24455         this.fireEvent('initial', this, this.gMapContext.location);
24456     },
24457     
24458     initOverlayView: function()
24459     {
24460         var _this = this;
24461         
24462         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24463             
24464             draw: function()
24465             {
24466                 _this.fireEvent('OverlayViewDraw', _this);
24467             },
24468             
24469             onAdd: function()
24470             {
24471                 _this.fireEvent('OverlayViewOnAdd', _this);
24472             },
24473             
24474             onRemove: function()
24475             {
24476                 _this.fireEvent('OverlayViewOnRemove', _this);
24477             },
24478             
24479             show: function(cpx)
24480             {
24481                 _this.fireEvent('OverlayViewShow', _this, cpx);
24482             },
24483             
24484             hide: function()
24485             {
24486                 _this.fireEvent('OverlayViewHide', _this);
24487             }
24488             
24489         });
24490     },
24491     
24492     fromLatLngToContainerPixel: function(event)
24493     {
24494         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24495     },
24496     
24497     isApplied: function() 
24498     {
24499         return this.getGmapContext() == false ? false : true;
24500     },
24501     
24502     getGmapContext: function() 
24503     {
24504         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24505     },
24506     
24507     GMapContext: function() 
24508     {
24509         var position = new google.maps.LatLng(this.latitude, this.longitude);
24510         
24511         var _map = new google.maps.Map(this.el.dom, {
24512             center: position,
24513             zoom: this.zoom,
24514             mapTypeId: this.mapTypeId,
24515             mapTypeControl: this.mapTypeControl,
24516             disableDoubleClickZoom: this.disableDoubleClickZoom,
24517             scrollwheel: this.scrollwheel,
24518             streetViewControl: this.streetViewControl,
24519             locationName: this.locationName,
24520             draggable: this.draggable,
24521             enableAutocomplete: this.enableAutocomplete,
24522             enableReverseGeocode: this.enableReverseGeocode
24523         });
24524         
24525         var _marker = new google.maps.Marker({
24526             position: position,
24527             map: _map,
24528             title: this.markerTitle,
24529             draggable: this.draggable
24530         });
24531         
24532         return {
24533             map: _map,
24534             marker: _marker,
24535             circle: null,
24536             location: position,
24537             radius: this.radius,
24538             locationName: this.locationName,
24539             addressComponents: {
24540                 formatted_address: null,
24541                 addressLine1: null,
24542                 addressLine2: null,
24543                 streetName: null,
24544                 streetNumber: null,
24545                 city: null,
24546                 district: null,
24547                 state: null,
24548                 stateOrProvince: null
24549             },
24550             settings: this,
24551             domContainer: this.el.dom,
24552             geodecoder: new google.maps.Geocoder()
24553         };
24554     },
24555     
24556     drawCircle: function(center, radius, options) 
24557     {
24558         if (this.gMapContext.circle != null) {
24559             this.gMapContext.circle.setMap(null);
24560         }
24561         if (radius > 0) {
24562             radius *= 1;
24563             options = Roo.apply({}, options, {
24564                 strokeColor: "#0000FF",
24565                 strokeOpacity: .35,
24566                 strokeWeight: 2,
24567                 fillColor: "#0000FF",
24568                 fillOpacity: .2
24569             });
24570             
24571             options.map = this.gMapContext.map;
24572             options.radius = radius;
24573             options.center = center;
24574             this.gMapContext.circle = new google.maps.Circle(options);
24575             return this.gMapContext.circle;
24576         }
24577         
24578         return null;
24579     },
24580     
24581     setPosition: function(location) 
24582     {
24583         this.gMapContext.location = location;
24584         this.gMapContext.marker.setPosition(location);
24585         this.gMapContext.map.panTo(location);
24586         this.drawCircle(location, this.gMapContext.radius, {});
24587         
24588         var _this = this;
24589         
24590         if (this.gMapContext.settings.enableReverseGeocode) {
24591             this.gMapContext.geodecoder.geocode({
24592                 latLng: this.gMapContext.location
24593             }, function(results, status) {
24594                 
24595                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24596                     _this.gMapContext.locationName = results[0].formatted_address;
24597                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24598                     
24599                     _this.fireEvent('positionchanged', this, location);
24600                 }
24601             });
24602             
24603             return;
24604         }
24605         
24606         this.fireEvent('positionchanged', this, location);
24607     },
24608     
24609     resize: function()
24610     {
24611         google.maps.event.trigger(this.gMapContext.map, "resize");
24612         
24613         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24614         
24615         this.fireEvent('resize', this);
24616     },
24617     
24618     setPositionByLatLng: function(latitude, longitude)
24619     {
24620         this.setPosition(new google.maps.LatLng(latitude, longitude));
24621     },
24622     
24623     getCurrentPosition: function() 
24624     {
24625         return {
24626             latitude: this.gMapContext.location.lat(),
24627             longitude: this.gMapContext.location.lng()
24628         };
24629     },
24630     
24631     getAddressName: function() 
24632     {
24633         return this.gMapContext.locationName;
24634     },
24635     
24636     getAddressComponents: function() 
24637     {
24638         return this.gMapContext.addressComponents;
24639     },
24640     
24641     address_component_from_google_geocode: function(address_components) 
24642     {
24643         var result = {};
24644         
24645         for (var i = 0; i < address_components.length; i++) {
24646             var component = address_components[i];
24647             if (component.types.indexOf("postal_code") >= 0) {
24648                 result.postalCode = component.short_name;
24649             } else if (component.types.indexOf("street_number") >= 0) {
24650                 result.streetNumber = component.short_name;
24651             } else if (component.types.indexOf("route") >= 0) {
24652                 result.streetName = component.short_name;
24653             } else if (component.types.indexOf("neighborhood") >= 0) {
24654                 result.city = component.short_name;
24655             } else if (component.types.indexOf("locality") >= 0) {
24656                 result.city = component.short_name;
24657             } else if (component.types.indexOf("sublocality") >= 0) {
24658                 result.district = component.short_name;
24659             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24660                 result.stateOrProvince = component.short_name;
24661             } else if (component.types.indexOf("country") >= 0) {
24662                 result.country = component.short_name;
24663             }
24664         }
24665         
24666         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24667         result.addressLine2 = "";
24668         return result;
24669     },
24670     
24671     setZoomLevel: function(zoom)
24672     {
24673         this.gMapContext.map.setZoom(zoom);
24674     },
24675     
24676     show: function()
24677     {
24678         if(!this.el){
24679             return;
24680         }
24681         
24682         this.el.show();
24683         
24684         this.resize();
24685         
24686         this.fireEvent('show', this);
24687     },
24688     
24689     hide: function()
24690     {
24691         if(!this.el){
24692             return;
24693         }
24694         
24695         this.el.hide();
24696         
24697         this.fireEvent('hide', this);
24698     }
24699     
24700 });
24701
24702 Roo.apply(Roo.bootstrap.LocationPicker, {
24703     
24704     OverlayView : function(map, options)
24705     {
24706         options = options || {};
24707         
24708         this.setMap(map);
24709     }
24710     
24711     
24712 });/*
24713  * - LGPL
24714  *
24715  * Alert
24716  * 
24717  */
24718
24719 /**
24720  * @class Roo.bootstrap.Alert
24721  * @extends Roo.bootstrap.Component
24722  * Bootstrap Alert class
24723  * @cfg {String} title The title of alert
24724  * @cfg {String} html The content of alert
24725  * @cfg {String} weight (  success | info | warning | danger )
24726  * @cfg {String} faicon font-awesomeicon
24727  * 
24728  * @constructor
24729  * Create a new alert
24730  * @param {Object} config The config object
24731  */
24732
24733
24734 Roo.bootstrap.Alert = function(config){
24735     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24736     
24737 };
24738
24739 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24740     
24741     title: '',
24742     html: '',
24743     weight: false,
24744     faicon: false,
24745     
24746     getAutoCreate : function()
24747     {
24748         
24749         var cfg = {
24750             tag : 'div',
24751             cls : 'alert',
24752             cn : [
24753                 {
24754                     tag : 'i',
24755                     cls : 'roo-alert-icon'
24756                     
24757                 },
24758                 {
24759                     tag : 'b',
24760                     cls : 'roo-alert-title',
24761                     html : this.title
24762                 },
24763                 {
24764                     tag : 'span',
24765                     cls : 'roo-alert-text',
24766                     html : this.html
24767                 }
24768             ]
24769         };
24770         
24771         if(this.faicon){
24772             cfg.cn[0].cls += ' fa ' + this.faicon;
24773         }
24774         
24775         if(this.weight){
24776             cfg.cls += ' alert-' + this.weight;
24777         }
24778         
24779         return cfg;
24780     },
24781     
24782     initEvents: function() 
24783     {
24784         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24785     },
24786     
24787     setTitle : function(str)
24788     {
24789         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24790     },
24791     
24792     setText : function(str)
24793     {
24794         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24795     },
24796     
24797     setWeight : function(weight)
24798     {
24799         if(this.weight){
24800             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24801         }
24802         
24803         this.weight = weight;
24804         
24805         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24806     },
24807     
24808     setIcon : function(icon)
24809     {
24810         if(this.faicon){
24811             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24812         }
24813         
24814         this.faicon = icon;
24815         
24816         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24817     },
24818     
24819     hide: function() 
24820     {
24821         this.el.hide();   
24822     },
24823     
24824     show: function() 
24825     {  
24826         this.el.show();   
24827     }
24828     
24829 });
24830
24831  
24832 /*
24833 * Licence: LGPL
24834 */
24835
24836 /**
24837  * @class Roo.bootstrap.UploadCropbox
24838  * @extends Roo.bootstrap.Component
24839  * Bootstrap UploadCropbox class
24840  * @cfg {String} emptyText show when image has been loaded
24841  * @cfg {String} rotateNotify show when image too small to rotate
24842  * @cfg {Number} errorTimeout default 3000
24843  * @cfg {Number} minWidth default 300
24844  * @cfg {Number} minHeight default 300
24845  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24846  * @cfg {Boolean} isDocument (true|false) default false
24847  * @cfg {String} url action url
24848  * @cfg {String} paramName default 'imageUpload'
24849  * @cfg {String} method default POST
24850  * @cfg {Boolean} loadMask (true|false) default true
24851  * @cfg {Boolean} loadingText default 'Loading...'
24852  * 
24853  * @constructor
24854  * Create a new UploadCropbox
24855  * @param {Object} config The config object
24856  */
24857
24858 Roo.bootstrap.UploadCropbox = function(config){
24859     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24860     
24861     this.addEvents({
24862         /**
24863          * @event beforeselectfile
24864          * Fire before select file
24865          * @param {Roo.bootstrap.UploadCropbox} this
24866          */
24867         "beforeselectfile" : true,
24868         /**
24869          * @event initial
24870          * Fire after initEvent
24871          * @param {Roo.bootstrap.UploadCropbox} this
24872          */
24873         "initial" : true,
24874         /**
24875          * @event crop
24876          * Fire after initEvent
24877          * @param {Roo.bootstrap.UploadCropbox} this
24878          * @param {String} data
24879          */
24880         "crop" : true,
24881         /**
24882          * @event prepare
24883          * Fire when preparing the file data
24884          * @param {Roo.bootstrap.UploadCropbox} this
24885          * @param {Object} file
24886          */
24887         "prepare" : true,
24888         /**
24889          * @event exception
24890          * Fire when get exception
24891          * @param {Roo.bootstrap.UploadCropbox} this
24892          * @param {XMLHttpRequest} xhr
24893          */
24894         "exception" : true,
24895         /**
24896          * @event beforeloadcanvas
24897          * Fire before load the canvas
24898          * @param {Roo.bootstrap.UploadCropbox} this
24899          * @param {String} src
24900          */
24901         "beforeloadcanvas" : true,
24902         /**
24903          * @event trash
24904          * Fire when trash image
24905          * @param {Roo.bootstrap.UploadCropbox} this
24906          */
24907         "trash" : true,
24908         /**
24909          * @event download
24910          * Fire when download the image
24911          * @param {Roo.bootstrap.UploadCropbox} this
24912          */
24913         "download" : true,
24914         /**
24915          * @event footerbuttonclick
24916          * Fire when footerbuttonclick
24917          * @param {Roo.bootstrap.UploadCropbox} this
24918          * @param {String} type
24919          */
24920         "footerbuttonclick" : true,
24921         /**
24922          * @event resize
24923          * Fire when resize
24924          * @param {Roo.bootstrap.UploadCropbox} this
24925          */
24926         "resize" : true,
24927         /**
24928          * @event rotate
24929          * Fire when rotate the image
24930          * @param {Roo.bootstrap.UploadCropbox} this
24931          * @param {String} pos
24932          */
24933         "rotate" : true,
24934         /**
24935          * @event inspect
24936          * Fire when inspect the file
24937          * @param {Roo.bootstrap.UploadCropbox} this
24938          * @param {Object} file
24939          */
24940         "inspect" : true,
24941         /**
24942          * @event upload
24943          * Fire when xhr upload the file
24944          * @param {Roo.bootstrap.UploadCropbox} this
24945          * @param {Object} data
24946          */
24947         "upload" : true,
24948         /**
24949          * @event arrange
24950          * Fire when arrange the file data
24951          * @param {Roo.bootstrap.UploadCropbox} this
24952          * @param {Object} formData
24953          */
24954         "arrange" : true
24955     });
24956     
24957     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24958 };
24959
24960 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24961     
24962     emptyText : 'Click to upload image',
24963     rotateNotify : 'Image is too small to rotate',
24964     errorTimeout : 3000,
24965     scale : 0,
24966     baseScale : 1,
24967     rotate : 0,
24968     dragable : false,
24969     pinching : false,
24970     mouseX : 0,
24971     mouseY : 0,
24972     cropData : false,
24973     minWidth : 300,
24974     minHeight : 300,
24975     file : false,
24976     exif : {},
24977     baseRotate : 1,
24978     cropType : 'image/jpeg',
24979     buttons : false,
24980     canvasLoaded : false,
24981     isDocument : false,
24982     method : 'POST',
24983     paramName : 'imageUpload',
24984     loadMask : true,
24985     loadingText : 'Loading...',
24986     maskEl : false,
24987     
24988     getAutoCreate : function()
24989     {
24990         var cfg = {
24991             tag : 'div',
24992             cls : 'roo-upload-cropbox',
24993             cn : [
24994                 {
24995                     tag : 'input',
24996                     cls : 'roo-upload-cropbox-selector',
24997                     type : 'file'
24998                 },
24999                 {
25000                     tag : 'div',
25001                     cls : 'roo-upload-cropbox-body',
25002                     style : 'cursor:pointer',
25003                     cn : [
25004                         {
25005                             tag : 'div',
25006                             cls : 'roo-upload-cropbox-preview'
25007                         },
25008                         {
25009                             tag : 'div',
25010                             cls : 'roo-upload-cropbox-thumb'
25011                         },
25012                         {
25013                             tag : 'div',
25014                             cls : 'roo-upload-cropbox-empty-notify',
25015                             html : this.emptyText
25016                         },
25017                         {
25018                             tag : 'div',
25019                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25020                             html : this.rotateNotify
25021                         }
25022                     ]
25023                 },
25024                 {
25025                     tag : 'div',
25026                     cls : 'roo-upload-cropbox-footer',
25027                     cn : {
25028                         tag : 'div',
25029                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25030                         cn : []
25031                     }
25032                 }
25033             ]
25034         };
25035         
25036         return cfg;
25037     },
25038     
25039     onRender : function(ct, position)
25040     {
25041         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25042         
25043         if (this.buttons.length) {
25044             
25045             Roo.each(this.buttons, function(bb) {
25046                 
25047                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25048                 
25049                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25050                 
25051             }, this);
25052         }
25053         
25054         if(this.loadMask){
25055             this.maskEl = this.el;
25056         }
25057     },
25058     
25059     initEvents : function()
25060     {
25061         this.urlAPI = (window.createObjectURL && window) || 
25062                                 (window.URL && URL.revokeObjectURL && URL) || 
25063                                 (window.webkitURL && webkitURL);
25064                         
25065         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25066         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25067         
25068         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25069         this.selectorEl.hide();
25070         
25071         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25072         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25073         
25074         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25075         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25076         this.thumbEl.hide();
25077         
25078         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25079         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25080         
25081         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25082         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25083         this.errorEl.hide();
25084         
25085         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25086         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25087         this.footerEl.hide();
25088         
25089         this.setThumbBoxSize();
25090         
25091         this.bind();
25092         
25093         this.resize();
25094         
25095         this.fireEvent('initial', this);
25096     },
25097
25098     bind : function()
25099     {
25100         var _this = this;
25101         
25102         window.addEventListener("resize", function() { _this.resize(); } );
25103         
25104         this.bodyEl.on('click', this.beforeSelectFile, this);
25105         
25106         if(Roo.isTouch){
25107             this.bodyEl.on('touchstart', this.onTouchStart, this);
25108             this.bodyEl.on('touchmove', this.onTouchMove, this);
25109             this.bodyEl.on('touchend', this.onTouchEnd, this);
25110         }
25111         
25112         if(!Roo.isTouch){
25113             this.bodyEl.on('mousedown', this.onMouseDown, this);
25114             this.bodyEl.on('mousemove', this.onMouseMove, this);
25115             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25116             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25117             Roo.get(document).on('mouseup', this.onMouseUp, this);
25118         }
25119         
25120         this.selectorEl.on('change', this.onFileSelected, this);
25121     },
25122     
25123     reset : function()
25124     {    
25125         this.scale = 0;
25126         this.baseScale = 1;
25127         this.rotate = 0;
25128         this.baseRotate = 1;
25129         this.dragable = false;
25130         this.pinching = false;
25131         this.mouseX = 0;
25132         this.mouseY = 0;
25133         this.cropData = false;
25134         this.notifyEl.dom.innerHTML = this.emptyText;
25135         
25136         this.selectorEl.dom.value = '';
25137         
25138     },
25139     
25140     resize : function()
25141     {
25142         if(this.fireEvent('resize', this) != false){
25143             this.setThumbBoxPosition();
25144             this.setCanvasPosition();
25145         }
25146     },
25147     
25148     onFooterButtonClick : function(e, el, o, type)
25149     {
25150         switch (type) {
25151             case 'rotate-left' :
25152                 this.onRotateLeft(e);
25153                 break;
25154             case 'rotate-right' :
25155                 this.onRotateRight(e);
25156                 break;
25157             case 'picture' :
25158                 this.beforeSelectFile(e);
25159                 break;
25160             case 'trash' :
25161                 this.trash(e);
25162                 break;
25163             case 'crop' :
25164                 this.crop(e);
25165                 break;
25166             case 'download' :
25167                 this.download(e);
25168                 break;
25169             default :
25170                 break;
25171         }
25172         
25173         this.fireEvent('footerbuttonclick', this, type);
25174     },
25175     
25176     beforeSelectFile : function(e)
25177     {
25178         e.preventDefault();
25179         
25180         if(this.fireEvent('beforeselectfile', this) != false){
25181             this.selectorEl.dom.click();
25182         }
25183     },
25184     
25185     onFileSelected : function(e)
25186     {
25187         e.preventDefault();
25188         
25189         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25190             return;
25191         }
25192         
25193         var file = this.selectorEl.dom.files[0];
25194         
25195         if(this.fireEvent('inspect', this, file) != false){
25196             this.prepare(file);
25197         }
25198         
25199     },
25200     
25201     trash : function(e)
25202     {
25203         this.fireEvent('trash', this);
25204     },
25205     
25206     download : function(e)
25207     {
25208         this.fireEvent('download', this);
25209     },
25210     
25211     loadCanvas : function(src)
25212     {   
25213         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25214             
25215             this.reset();
25216             
25217             this.imageEl = document.createElement('img');
25218             
25219             var _this = this;
25220             
25221             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25222             
25223             this.imageEl.src = src;
25224         }
25225     },
25226     
25227     onLoadCanvas : function()
25228     {   
25229         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25230         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25231         
25232         this.bodyEl.un('click', this.beforeSelectFile, this);
25233         
25234         this.notifyEl.hide();
25235         this.thumbEl.show();
25236         this.footerEl.show();
25237         
25238         this.baseRotateLevel();
25239         
25240         if(this.isDocument){
25241             this.setThumbBoxSize();
25242         }
25243         
25244         this.setThumbBoxPosition();
25245         
25246         this.baseScaleLevel();
25247         
25248         this.draw();
25249         
25250         this.resize();
25251         
25252         this.canvasLoaded = true;
25253         
25254         if(this.loadMask){
25255             this.maskEl.unmask();
25256         }
25257         
25258     },
25259     
25260     setCanvasPosition : function()
25261     {   
25262         if(!this.canvasEl){
25263             return;
25264         }
25265         
25266         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25267         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25268         
25269         this.previewEl.setLeft(pw);
25270         this.previewEl.setTop(ph);
25271         
25272     },
25273     
25274     onMouseDown : function(e)
25275     {   
25276         e.stopEvent();
25277         
25278         this.dragable = true;
25279         this.pinching = false;
25280         
25281         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25282             this.dragable = false;
25283             return;
25284         }
25285         
25286         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25287         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25288         
25289     },
25290     
25291     onMouseMove : function(e)
25292     {   
25293         e.stopEvent();
25294         
25295         if(!this.canvasLoaded){
25296             return;
25297         }
25298         
25299         if (!this.dragable){
25300             return;
25301         }
25302         
25303         var minX = Math.ceil(this.thumbEl.getLeft(true));
25304         var minY = Math.ceil(this.thumbEl.getTop(true));
25305         
25306         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25307         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25308         
25309         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25310         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25311         
25312         x = x - this.mouseX;
25313         y = y - this.mouseY;
25314         
25315         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25316         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25317         
25318         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25319         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25320         
25321         this.previewEl.setLeft(bgX);
25322         this.previewEl.setTop(bgY);
25323         
25324         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25325         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25326     },
25327     
25328     onMouseUp : function(e)
25329     {   
25330         e.stopEvent();
25331         
25332         this.dragable = false;
25333     },
25334     
25335     onMouseWheel : function(e)
25336     {   
25337         e.stopEvent();
25338         
25339         this.startScale = this.scale;
25340         
25341         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25342         
25343         if(!this.zoomable()){
25344             this.scale = this.startScale;
25345             return;
25346         }
25347         
25348         this.draw();
25349         
25350         return;
25351     },
25352     
25353     zoomable : function()
25354     {
25355         var minScale = this.thumbEl.getWidth() / this.minWidth;
25356         
25357         if(this.minWidth < this.minHeight){
25358             minScale = this.thumbEl.getHeight() / this.minHeight;
25359         }
25360         
25361         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25362         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25363         
25364         if(
25365                 this.isDocument &&
25366                 (this.rotate == 0 || this.rotate == 180) && 
25367                 (
25368                     width > this.imageEl.OriginWidth || 
25369                     height > this.imageEl.OriginHeight ||
25370                     (width < this.minWidth && height < this.minHeight)
25371                 )
25372         ){
25373             return false;
25374         }
25375         
25376         if(
25377                 this.isDocument &&
25378                 (this.rotate == 90 || this.rotate == 270) && 
25379                 (
25380                     width > this.imageEl.OriginWidth || 
25381                     height > this.imageEl.OriginHeight ||
25382                     (width < this.minHeight && height < this.minWidth)
25383                 )
25384         ){
25385             return false;
25386         }
25387         
25388         if(
25389                 !this.isDocument &&
25390                 (this.rotate == 0 || this.rotate == 180) && 
25391                 (
25392                     width < this.minWidth || 
25393                     width > this.imageEl.OriginWidth || 
25394                     height < this.minHeight || 
25395                     height > this.imageEl.OriginHeight
25396                 )
25397         ){
25398             return false;
25399         }
25400         
25401         if(
25402                 !this.isDocument &&
25403                 (this.rotate == 90 || this.rotate == 270) && 
25404                 (
25405                     width < this.minHeight || 
25406                     width > this.imageEl.OriginWidth || 
25407                     height < this.minWidth || 
25408                     height > this.imageEl.OriginHeight
25409                 )
25410         ){
25411             return false;
25412         }
25413         
25414         return true;
25415         
25416     },
25417     
25418     onRotateLeft : function(e)
25419     {   
25420         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25421             
25422             var minScale = this.thumbEl.getWidth() / this.minWidth;
25423             
25424             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25425             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25426             
25427             this.startScale = this.scale;
25428             
25429             while (this.getScaleLevel() < minScale){
25430             
25431                 this.scale = this.scale + 1;
25432                 
25433                 if(!this.zoomable()){
25434                     break;
25435                 }
25436                 
25437                 if(
25438                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25439                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25440                 ){
25441                     continue;
25442                 }
25443                 
25444                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25445
25446                 this.draw();
25447                 
25448                 return;
25449             }
25450             
25451             this.scale = this.startScale;
25452             
25453             this.onRotateFail();
25454             
25455             return false;
25456         }
25457         
25458         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25459
25460         if(this.isDocument){
25461             this.setThumbBoxSize();
25462             this.setThumbBoxPosition();
25463             this.setCanvasPosition();
25464         }
25465         
25466         this.draw();
25467         
25468         this.fireEvent('rotate', this, 'left');
25469         
25470     },
25471     
25472     onRotateRight : function(e)
25473     {
25474         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25475             
25476             var minScale = this.thumbEl.getWidth() / this.minWidth;
25477         
25478             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25479             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25480             
25481             this.startScale = this.scale;
25482             
25483             while (this.getScaleLevel() < minScale){
25484             
25485                 this.scale = this.scale + 1;
25486                 
25487                 if(!this.zoomable()){
25488                     break;
25489                 }
25490                 
25491                 if(
25492                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25493                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25494                 ){
25495                     continue;
25496                 }
25497                 
25498                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25499
25500                 this.draw();
25501                 
25502                 return;
25503             }
25504             
25505             this.scale = this.startScale;
25506             
25507             this.onRotateFail();
25508             
25509             return false;
25510         }
25511         
25512         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25513
25514         if(this.isDocument){
25515             this.setThumbBoxSize();
25516             this.setThumbBoxPosition();
25517             this.setCanvasPosition();
25518         }
25519         
25520         this.draw();
25521         
25522         this.fireEvent('rotate', this, 'right');
25523     },
25524     
25525     onRotateFail : function()
25526     {
25527         this.errorEl.show(true);
25528         
25529         var _this = this;
25530         
25531         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25532     },
25533     
25534     draw : function()
25535     {
25536         this.previewEl.dom.innerHTML = '';
25537         
25538         var canvasEl = document.createElement("canvas");
25539         
25540         var contextEl = canvasEl.getContext("2d");
25541         
25542         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25543         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25544         var center = this.imageEl.OriginWidth / 2;
25545         
25546         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25547             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25548             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25549             center = this.imageEl.OriginHeight / 2;
25550         }
25551         
25552         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25553         
25554         contextEl.translate(center, center);
25555         contextEl.rotate(this.rotate * Math.PI / 180);
25556
25557         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25558         
25559         this.canvasEl = document.createElement("canvas");
25560         
25561         this.contextEl = this.canvasEl.getContext("2d");
25562         
25563         switch (this.rotate) {
25564             case 0 :
25565                 
25566                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25567                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25568                 
25569                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25570                 
25571                 break;
25572             case 90 : 
25573                 
25574                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25575                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25576                 
25577                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25578                     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);
25579                     break;
25580                 }
25581                 
25582                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25583                 
25584                 break;
25585             case 180 :
25586                 
25587                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25588                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25589                 
25590                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25591                     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);
25592                     break;
25593                 }
25594                 
25595                 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);
25596                 
25597                 break;
25598             case 270 :
25599                 
25600                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25601                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25602         
25603                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25604                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25605                     break;
25606                 }
25607                 
25608                 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);
25609                 
25610                 break;
25611             default : 
25612                 break;
25613         }
25614         
25615         this.previewEl.appendChild(this.canvasEl);
25616         
25617         this.setCanvasPosition();
25618     },
25619     
25620     crop : function()
25621     {
25622         if(!this.canvasLoaded){
25623             return;
25624         }
25625         
25626         var imageCanvas = document.createElement("canvas");
25627         
25628         var imageContext = imageCanvas.getContext("2d");
25629         
25630         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25631         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25632         
25633         var center = imageCanvas.width / 2;
25634         
25635         imageContext.translate(center, center);
25636         
25637         imageContext.rotate(this.rotate * Math.PI / 180);
25638         
25639         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25640         
25641         var canvas = document.createElement("canvas");
25642         
25643         var context = canvas.getContext("2d");
25644                 
25645         canvas.width = this.minWidth;
25646         canvas.height = this.minHeight;
25647
25648         switch (this.rotate) {
25649             case 0 :
25650                 
25651                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25652                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25653                 
25654                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25655                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25656                 
25657                 var targetWidth = this.minWidth - 2 * x;
25658                 var targetHeight = this.minHeight - 2 * y;
25659                 
25660                 var scale = 1;
25661                 
25662                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25663                     scale = targetWidth / width;
25664                 }
25665                 
25666                 if(x > 0 && y == 0){
25667                     scale = targetHeight / height;
25668                 }
25669                 
25670                 if(x > 0 && y > 0){
25671                     scale = targetWidth / width;
25672                     
25673                     if(width < height){
25674                         scale = targetHeight / height;
25675                     }
25676                 }
25677                 
25678                 context.scale(scale, scale);
25679                 
25680                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25681                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25682
25683                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25684                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25685
25686                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25687                 
25688                 break;
25689             case 90 : 
25690                 
25691                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25692                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25693                 
25694                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25695                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25696                 
25697                 var targetWidth = this.minWidth - 2 * x;
25698                 var targetHeight = this.minHeight - 2 * y;
25699                 
25700                 var scale = 1;
25701                 
25702                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25703                     scale = targetWidth / width;
25704                 }
25705                 
25706                 if(x > 0 && y == 0){
25707                     scale = targetHeight / height;
25708                 }
25709                 
25710                 if(x > 0 && y > 0){
25711                     scale = targetWidth / width;
25712                     
25713                     if(width < height){
25714                         scale = targetHeight / height;
25715                     }
25716                 }
25717                 
25718                 context.scale(scale, scale);
25719                 
25720                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25721                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25722
25723                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25724                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25725                 
25726                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25727                 
25728                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25729                 
25730                 break;
25731             case 180 :
25732                 
25733                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25734                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25735                 
25736                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25737                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25738                 
25739                 var targetWidth = this.minWidth - 2 * x;
25740                 var targetHeight = this.minHeight - 2 * y;
25741                 
25742                 var scale = 1;
25743                 
25744                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25745                     scale = targetWidth / width;
25746                 }
25747                 
25748                 if(x > 0 && y == 0){
25749                     scale = targetHeight / height;
25750                 }
25751                 
25752                 if(x > 0 && y > 0){
25753                     scale = targetWidth / width;
25754                     
25755                     if(width < height){
25756                         scale = targetHeight / height;
25757                     }
25758                 }
25759                 
25760                 context.scale(scale, scale);
25761                 
25762                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25763                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25764
25765                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25766                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25767
25768                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25769                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25770                 
25771                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25772                 
25773                 break;
25774             case 270 :
25775                 
25776                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25777                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25778                 
25779                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25780                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25781                 
25782                 var targetWidth = this.minWidth - 2 * x;
25783                 var targetHeight = this.minHeight - 2 * y;
25784                 
25785                 var scale = 1;
25786                 
25787                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25788                     scale = targetWidth / width;
25789                 }
25790                 
25791                 if(x > 0 && y == 0){
25792                     scale = targetHeight / height;
25793                 }
25794                 
25795                 if(x > 0 && y > 0){
25796                     scale = targetWidth / width;
25797                     
25798                     if(width < height){
25799                         scale = targetHeight / height;
25800                     }
25801                 }
25802                 
25803                 context.scale(scale, scale);
25804                 
25805                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25806                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25807
25808                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25809                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25810                 
25811                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25812                 
25813                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25814                 
25815                 break;
25816             default : 
25817                 break;
25818         }
25819         
25820         this.cropData = canvas.toDataURL(this.cropType);
25821         
25822         if(this.fireEvent('crop', this, this.cropData) !== false){
25823             this.process(this.file, this.cropData);
25824         }
25825         
25826         return;
25827         
25828     },
25829     
25830     setThumbBoxSize : function()
25831     {
25832         var width, height;
25833         
25834         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25835             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25836             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25837             
25838             this.minWidth = width;
25839             this.minHeight = height;
25840             
25841             if(this.rotate == 90 || this.rotate == 270){
25842                 this.minWidth = height;
25843                 this.minHeight = width;
25844             }
25845         }
25846         
25847         height = 300;
25848         width = Math.ceil(this.minWidth * height / this.minHeight);
25849         
25850         if(this.minWidth > this.minHeight){
25851             width = 300;
25852             height = Math.ceil(this.minHeight * width / this.minWidth);
25853         }
25854         
25855         this.thumbEl.setStyle({
25856             width : width + 'px',
25857             height : height + 'px'
25858         });
25859
25860         return;
25861             
25862     },
25863     
25864     setThumbBoxPosition : function()
25865     {
25866         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25867         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25868         
25869         this.thumbEl.setLeft(x);
25870         this.thumbEl.setTop(y);
25871         
25872     },
25873     
25874     baseRotateLevel : function()
25875     {
25876         this.baseRotate = 1;
25877         
25878         if(
25879                 typeof(this.exif) != 'undefined' &&
25880                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25881                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25882         ){
25883             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25884         }
25885         
25886         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25887         
25888     },
25889     
25890     baseScaleLevel : function()
25891     {
25892         var width, height;
25893         
25894         if(this.isDocument){
25895             
25896             if(this.baseRotate == 6 || this.baseRotate == 8){
25897             
25898                 height = this.thumbEl.getHeight();
25899                 this.baseScale = height / this.imageEl.OriginWidth;
25900
25901                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25902                     width = this.thumbEl.getWidth();
25903                     this.baseScale = width / this.imageEl.OriginHeight;
25904                 }
25905
25906                 return;
25907             }
25908
25909             height = this.thumbEl.getHeight();
25910             this.baseScale = height / this.imageEl.OriginHeight;
25911
25912             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25913                 width = this.thumbEl.getWidth();
25914                 this.baseScale = width / this.imageEl.OriginWidth;
25915             }
25916
25917             return;
25918         }
25919         
25920         if(this.baseRotate == 6 || this.baseRotate == 8){
25921             
25922             width = this.thumbEl.getHeight();
25923             this.baseScale = width / this.imageEl.OriginHeight;
25924             
25925             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25926                 height = this.thumbEl.getWidth();
25927                 this.baseScale = height / this.imageEl.OriginHeight;
25928             }
25929             
25930             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25931                 height = this.thumbEl.getWidth();
25932                 this.baseScale = height / this.imageEl.OriginHeight;
25933                 
25934                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25935                     width = this.thumbEl.getHeight();
25936                     this.baseScale = width / this.imageEl.OriginWidth;
25937                 }
25938             }
25939             
25940             return;
25941         }
25942         
25943         width = this.thumbEl.getWidth();
25944         this.baseScale = width / this.imageEl.OriginWidth;
25945         
25946         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25947             height = this.thumbEl.getHeight();
25948             this.baseScale = height / this.imageEl.OriginHeight;
25949         }
25950         
25951         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25952             
25953             height = this.thumbEl.getHeight();
25954             this.baseScale = height / this.imageEl.OriginHeight;
25955             
25956             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25957                 width = this.thumbEl.getWidth();
25958                 this.baseScale = width / this.imageEl.OriginWidth;
25959             }
25960             
25961         }
25962         
25963         return;
25964     },
25965     
25966     getScaleLevel : function()
25967     {
25968         return this.baseScale * Math.pow(1.1, this.scale);
25969     },
25970     
25971     onTouchStart : function(e)
25972     {
25973         if(!this.canvasLoaded){
25974             this.beforeSelectFile(e);
25975             return;
25976         }
25977         
25978         var touches = e.browserEvent.touches;
25979         
25980         if(!touches){
25981             return;
25982         }
25983         
25984         if(touches.length == 1){
25985             this.onMouseDown(e);
25986             return;
25987         }
25988         
25989         if(touches.length != 2){
25990             return;
25991         }
25992         
25993         var coords = [];
25994         
25995         for(var i = 0, finger; finger = touches[i]; i++){
25996             coords.push(finger.pageX, finger.pageY);
25997         }
25998         
25999         var x = Math.pow(coords[0] - coords[2], 2);
26000         var y = Math.pow(coords[1] - coords[3], 2);
26001         
26002         this.startDistance = Math.sqrt(x + y);
26003         
26004         this.startScale = this.scale;
26005         
26006         this.pinching = true;
26007         this.dragable = false;
26008         
26009     },
26010     
26011     onTouchMove : function(e)
26012     {
26013         if(!this.pinching && !this.dragable){
26014             return;
26015         }
26016         
26017         var touches = e.browserEvent.touches;
26018         
26019         if(!touches){
26020             return;
26021         }
26022         
26023         if(this.dragable){
26024             this.onMouseMove(e);
26025             return;
26026         }
26027         
26028         var coords = [];
26029         
26030         for(var i = 0, finger; finger = touches[i]; i++){
26031             coords.push(finger.pageX, finger.pageY);
26032         }
26033         
26034         var x = Math.pow(coords[0] - coords[2], 2);
26035         var y = Math.pow(coords[1] - coords[3], 2);
26036         
26037         this.endDistance = Math.sqrt(x + y);
26038         
26039         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26040         
26041         if(!this.zoomable()){
26042             this.scale = this.startScale;
26043             return;
26044         }
26045         
26046         this.draw();
26047         
26048     },
26049     
26050     onTouchEnd : function(e)
26051     {
26052         this.pinching = false;
26053         this.dragable = false;
26054         
26055     },
26056     
26057     process : function(file, crop)
26058     {
26059         if(this.loadMask){
26060             this.maskEl.mask(this.loadingText);
26061         }
26062         
26063         this.xhr = new XMLHttpRequest();
26064         
26065         file.xhr = this.xhr;
26066
26067         this.xhr.open(this.method, this.url, true);
26068         
26069         var headers = {
26070             "Accept": "application/json",
26071             "Cache-Control": "no-cache",
26072             "X-Requested-With": "XMLHttpRequest"
26073         };
26074         
26075         for (var headerName in headers) {
26076             var headerValue = headers[headerName];
26077             if (headerValue) {
26078                 this.xhr.setRequestHeader(headerName, headerValue);
26079             }
26080         }
26081         
26082         var _this = this;
26083         
26084         this.xhr.onload = function()
26085         {
26086             _this.xhrOnLoad(_this.xhr);
26087         }
26088         
26089         this.xhr.onerror = function()
26090         {
26091             _this.xhrOnError(_this.xhr);
26092         }
26093         
26094         var formData = new FormData();
26095
26096         formData.append('returnHTML', 'NO');
26097         
26098         if(crop){
26099             formData.append('crop', crop);
26100         }
26101         
26102         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26103             formData.append(this.paramName, file, file.name);
26104         }
26105         
26106         if(typeof(file.filename) != 'undefined'){
26107             formData.append('filename', file.filename);
26108         }
26109         
26110         if(typeof(file.mimetype) != 'undefined'){
26111             formData.append('mimetype', file.mimetype);
26112         }
26113         
26114         if(this.fireEvent('arrange', this, formData) != false){
26115             this.xhr.send(formData);
26116         };
26117     },
26118     
26119     xhrOnLoad : function(xhr)
26120     {
26121         if(this.loadMask){
26122             this.maskEl.unmask();
26123         }
26124         
26125         if (xhr.readyState !== 4) {
26126             this.fireEvent('exception', this, xhr);
26127             return;
26128         }
26129
26130         var response = Roo.decode(xhr.responseText);
26131         
26132         if(!response.success){
26133             this.fireEvent('exception', this, xhr);
26134             return;
26135         }
26136         
26137         var response = Roo.decode(xhr.responseText);
26138         
26139         this.fireEvent('upload', this, response);
26140         
26141     },
26142     
26143     xhrOnError : function()
26144     {
26145         if(this.loadMask){
26146             this.maskEl.unmask();
26147         }
26148         
26149         Roo.log('xhr on error');
26150         
26151         var response = Roo.decode(xhr.responseText);
26152           
26153         Roo.log(response);
26154         
26155     },
26156     
26157     prepare : function(file)
26158     {   
26159         if(this.loadMask){
26160             this.maskEl.mask(this.loadingText);
26161         }
26162         
26163         this.file = false;
26164         this.exif = {};
26165         
26166         if(typeof(file) === 'string'){
26167             this.loadCanvas(file);
26168             return;
26169         }
26170         
26171         if(!file || !this.urlAPI){
26172             return;
26173         }
26174         
26175         this.file = file;
26176         this.cropType = file.type;
26177         
26178         var _this = this;
26179         
26180         if(this.fireEvent('prepare', this, this.file) != false){
26181             
26182             var reader = new FileReader();
26183             
26184             reader.onload = function (e) {
26185                 if (e.target.error) {
26186                     Roo.log(e.target.error);
26187                     return;
26188                 }
26189                 
26190                 var buffer = e.target.result,
26191                     dataView = new DataView(buffer),
26192                     offset = 2,
26193                     maxOffset = dataView.byteLength - 4,
26194                     markerBytes,
26195                     markerLength;
26196                 
26197                 if (dataView.getUint16(0) === 0xffd8) {
26198                     while (offset < maxOffset) {
26199                         markerBytes = dataView.getUint16(offset);
26200                         
26201                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26202                             markerLength = dataView.getUint16(offset + 2) + 2;
26203                             if (offset + markerLength > dataView.byteLength) {
26204                                 Roo.log('Invalid meta data: Invalid segment size.');
26205                                 break;
26206                             }
26207                             
26208                             if(markerBytes == 0xffe1){
26209                                 _this.parseExifData(
26210                                     dataView,
26211                                     offset,
26212                                     markerLength
26213                                 );
26214                             }
26215                             
26216                             offset += markerLength;
26217                             
26218                             continue;
26219                         }
26220                         
26221                         break;
26222                     }
26223                     
26224                 }
26225                 
26226                 var url = _this.urlAPI.createObjectURL(_this.file);
26227                 
26228                 _this.loadCanvas(url);
26229                 
26230                 return;
26231             }
26232             
26233             reader.readAsArrayBuffer(this.file);
26234             
26235         }
26236         
26237     },
26238     
26239     parseExifData : function(dataView, offset, length)
26240     {
26241         var tiffOffset = offset + 10,
26242             littleEndian,
26243             dirOffset;
26244     
26245         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26246             // No Exif data, might be XMP data instead
26247             return;
26248         }
26249         
26250         // Check for the ASCII code for "Exif" (0x45786966):
26251         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26252             // No Exif data, might be XMP data instead
26253             return;
26254         }
26255         if (tiffOffset + 8 > dataView.byteLength) {
26256             Roo.log('Invalid Exif data: Invalid segment size.');
26257             return;
26258         }
26259         // Check for the two null bytes:
26260         if (dataView.getUint16(offset + 8) !== 0x0000) {
26261             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26262             return;
26263         }
26264         // Check the byte alignment:
26265         switch (dataView.getUint16(tiffOffset)) {
26266         case 0x4949:
26267             littleEndian = true;
26268             break;
26269         case 0x4D4D:
26270             littleEndian = false;
26271             break;
26272         default:
26273             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26274             return;
26275         }
26276         // Check for the TIFF tag marker (0x002A):
26277         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26278             Roo.log('Invalid Exif data: Missing TIFF marker.');
26279             return;
26280         }
26281         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26282         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26283         
26284         this.parseExifTags(
26285             dataView,
26286             tiffOffset,
26287             tiffOffset + dirOffset,
26288             littleEndian
26289         );
26290     },
26291     
26292     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26293     {
26294         var tagsNumber,
26295             dirEndOffset,
26296             i;
26297         if (dirOffset + 6 > dataView.byteLength) {
26298             Roo.log('Invalid Exif data: Invalid directory offset.');
26299             return;
26300         }
26301         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26302         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26303         if (dirEndOffset + 4 > dataView.byteLength) {
26304             Roo.log('Invalid Exif data: Invalid directory size.');
26305             return;
26306         }
26307         for (i = 0; i < tagsNumber; i += 1) {
26308             this.parseExifTag(
26309                 dataView,
26310                 tiffOffset,
26311                 dirOffset + 2 + 12 * i, // tag offset
26312                 littleEndian
26313             );
26314         }
26315         // Return the offset to the next directory:
26316         return dataView.getUint32(dirEndOffset, littleEndian);
26317     },
26318     
26319     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26320     {
26321         var tag = dataView.getUint16(offset, littleEndian);
26322         
26323         this.exif[tag] = this.getExifValue(
26324             dataView,
26325             tiffOffset,
26326             offset,
26327             dataView.getUint16(offset + 2, littleEndian), // tag type
26328             dataView.getUint32(offset + 4, littleEndian), // tag length
26329             littleEndian
26330         );
26331     },
26332     
26333     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26334     {
26335         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26336             tagSize,
26337             dataOffset,
26338             values,
26339             i,
26340             str,
26341             c;
26342     
26343         if (!tagType) {
26344             Roo.log('Invalid Exif data: Invalid tag type.');
26345             return;
26346         }
26347         
26348         tagSize = tagType.size * length;
26349         // Determine if the value is contained in the dataOffset bytes,
26350         // or if the value at the dataOffset is a pointer to the actual data:
26351         dataOffset = tagSize > 4 ?
26352                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26353         if (dataOffset + tagSize > dataView.byteLength) {
26354             Roo.log('Invalid Exif data: Invalid data offset.');
26355             return;
26356         }
26357         if (length === 1) {
26358             return tagType.getValue(dataView, dataOffset, littleEndian);
26359         }
26360         values = [];
26361         for (i = 0; i < length; i += 1) {
26362             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26363         }
26364         
26365         if (tagType.ascii) {
26366             str = '';
26367             // Concatenate the chars:
26368             for (i = 0; i < values.length; i += 1) {
26369                 c = values[i];
26370                 // Ignore the terminating NULL byte(s):
26371                 if (c === '\u0000') {
26372                     break;
26373                 }
26374                 str += c;
26375             }
26376             return str;
26377         }
26378         return values;
26379     }
26380     
26381 });
26382
26383 Roo.apply(Roo.bootstrap.UploadCropbox, {
26384     tags : {
26385         'Orientation': 0x0112
26386     },
26387     
26388     Orientation: {
26389             1: 0, //'top-left',
26390 //            2: 'top-right',
26391             3: 180, //'bottom-right',
26392 //            4: 'bottom-left',
26393 //            5: 'left-top',
26394             6: 90, //'right-top',
26395 //            7: 'right-bottom',
26396             8: 270 //'left-bottom'
26397     },
26398     
26399     exifTagTypes : {
26400         // byte, 8-bit unsigned int:
26401         1: {
26402             getValue: function (dataView, dataOffset) {
26403                 return dataView.getUint8(dataOffset);
26404             },
26405             size: 1
26406         },
26407         // ascii, 8-bit byte:
26408         2: {
26409             getValue: function (dataView, dataOffset) {
26410                 return String.fromCharCode(dataView.getUint8(dataOffset));
26411             },
26412             size: 1,
26413             ascii: true
26414         },
26415         // short, 16 bit int:
26416         3: {
26417             getValue: function (dataView, dataOffset, littleEndian) {
26418                 return dataView.getUint16(dataOffset, littleEndian);
26419             },
26420             size: 2
26421         },
26422         // long, 32 bit int:
26423         4: {
26424             getValue: function (dataView, dataOffset, littleEndian) {
26425                 return dataView.getUint32(dataOffset, littleEndian);
26426             },
26427             size: 4
26428         },
26429         // rational = two long values, first is numerator, second is denominator:
26430         5: {
26431             getValue: function (dataView, dataOffset, littleEndian) {
26432                 return dataView.getUint32(dataOffset, littleEndian) /
26433                     dataView.getUint32(dataOffset + 4, littleEndian);
26434             },
26435             size: 8
26436         },
26437         // slong, 32 bit signed int:
26438         9: {
26439             getValue: function (dataView, dataOffset, littleEndian) {
26440                 return dataView.getInt32(dataOffset, littleEndian);
26441             },
26442             size: 4
26443         },
26444         // srational, two slongs, first is numerator, second is denominator:
26445         10: {
26446             getValue: function (dataView, dataOffset, littleEndian) {
26447                 return dataView.getInt32(dataOffset, littleEndian) /
26448                     dataView.getInt32(dataOffset + 4, littleEndian);
26449             },
26450             size: 8
26451         }
26452     },
26453     
26454     footer : {
26455         STANDARD : [
26456             {
26457                 tag : 'div',
26458                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26459                 action : 'rotate-left',
26460                 cn : [
26461                     {
26462                         tag : 'button',
26463                         cls : 'btn btn-default',
26464                         html : '<i class="fa fa-undo"></i>'
26465                     }
26466                 ]
26467             },
26468             {
26469                 tag : 'div',
26470                 cls : 'btn-group roo-upload-cropbox-picture',
26471                 action : 'picture',
26472                 cn : [
26473                     {
26474                         tag : 'button',
26475                         cls : 'btn btn-default',
26476                         html : '<i class="fa fa-picture-o"></i>'
26477                     }
26478                 ]
26479             },
26480             {
26481                 tag : 'div',
26482                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26483                 action : 'rotate-right',
26484                 cn : [
26485                     {
26486                         tag : 'button',
26487                         cls : 'btn btn-default',
26488                         html : '<i class="fa fa-repeat"></i>'
26489                     }
26490                 ]
26491             }
26492         ],
26493         DOCUMENT : [
26494             {
26495                 tag : 'div',
26496                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26497                 action : 'rotate-left',
26498                 cn : [
26499                     {
26500                         tag : 'button',
26501                         cls : 'btn btn-default',
26502                         html : '<i class="fa fa-undo"></i>'
26503                     }
26504                 ]
26505             },
26506             {
26507                 tag : 'div',
26508                 cls : 'btn-group roo-upload-cropbox-download',
26509                 action : 'download',
26510                 cn : [
26511                     {
26512                         tag : 'button',
26513                         cls : 'btn btn-default',
26514                         html : '<i class="fa fa-download"></i>'
26515                     }
26516                 ]
26517             },
26518             {
26519                 tag : 'div',
26520                 cls : 'btn-group roo-upload-cropbox-crop',
26521                 action : 'crop',
26522                 cn : [
26523                     {
26524                         tag : 'button',
26525                         cls : 'btn btn-default',
26526                         html : '<i class="fa fa-crop"></i>'
26527                     }
26528                 ]
26529             },
26530             {
26531                 tag : 'div',
26532                 cls : 'btn-group roo-upload-cropbox-trash',
26533                 action : 'trash',
26534                 cn : [
26535                     {
26536                         tag : 'button',
26537                         cls : 'btn btn-default',
26538                         html : '<i class="fa fa-trash"></i>'
26539                     }
26540                 ]
26541             },
26542             {
26543                 tag : 'div',
26544                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26545                 action : 'rotate-right',
26546                 cn : [
26547                     {
26548                         tag : 'button',
26549                         cls : 'btn btn-default',
26550                         html : '<i class="fa fa-repeat"></i>'
26551                     }
26552                 ]
26553             }
26554         ],
26555         ROTATOR : [
26556             {
26557                 tag : 'div',
26558                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26559                 action : 'rotate-left',
26560                 cn : [
26561                     {
26562                         tag : 'button',
26563                         cls : 'btn btn-default',
26564                         html : '<i class="fa fa-undo"></i>'
26565                     }
26566                 ]
26567             },
26568             {
26569                 tag : 'div',
26570                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26571                 action : 'rotate-right',
26572                 cn : [
26573                     {
26574                         tag : 'button',
26575                         cls : 'btn btn-default',
26576                         html : '<i class="fa fa-repeat"></i>'
26577                     }
26578                 ]
26579             }
26580         ]
26581     }
26582 });
26583
26584 /*
26585 * Licence: LGPL
26586 */
26587
26588 /**
26589  * @class Roo.bootstrap.DocumentManager
26590  * @extends Roo.bootstrap.Component
26591  * Bootstrap DocumentManager class
26592  * @cfg {String} paramName default 'imageUpload'
26593  * @cfg {String} method default POST
26594  * @cfg {String} url action url
26595  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26596  * @cfg {Boolean} multiple multiple upload default true
26597  * @cfg {Number} thumbSize default 300
26598  * @cfg {String} fieldLabel
26599  * @cfg {Number} labelWidth default 4
26600  * @cfg {String} labelAlign (left|top) default left
26601  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26602  * 
26603  * @constructor
26604  * Create a new DocumentManager
26605  * @param {Object} config The config object
26606  */
26607
26608 Roo.bootstrap.DocumentManager = function(config){
26609     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26610     
26611     this.addEvents({
26612         /**
26613          * @event initial
26614          * Fire when initial the DocumentManager
26615          * @param {Roo.bootstrap.DocumentManager} this
26616          */
26617         "initial" : true,
26618         /**
26619          * @event inspect
26620          * inspect selected file
26621          * @param {Roo.bootstrap.DocumentManager} this
26622          * @param {File} file
26623          */
26624         "inspect" : true,
26625         /**
26626          * @event exception
26627          * Fire when xhr load exception
26628          * @param {Roo.bootstrap.DocumentManager} this
26629          * @param {XMLHttpRequest} xhr
26630          */
26631         "exception" : true,
26632         /**
26633          * @event prepare
26634          * prepare the form data
26635          * @param {Roo.bootstrap.DocumentManager} this
26636          * @param {Object} formData
26637          */
26638         "prepare" : true,
26639         /**
26640          * @event remove
26641          * Fire when remove the file
26642          * @param {Roo.bootstrap.DocumentManager} this
26643          * @param {Object} file
26644          */
26645         "remove" : true,
26646         /**
26647          * @event refresh
26648          * Fire after refresh the file
26649          * @param {Roo.bootstrap.DocumentManager} this
26650          */
26651         "refresh" : true,
26652         /**
26653          * @event click
26654          * Fire after click the image
26655          * @param {Roo.bootstrap.DocumentManager} this
26656          * @param {Object} file
26657          */
26658         "click" : true,
26659         /**
26660          * @event edit
26661          * Fire when upload a image and editable set to true
26662          * @param {Roo.bootstrap.DocumentManager} this
26663          * @param {Object} file
26664          */
26665         "edit" : true,
26666         /**
26667          * @event beforeselectfile
26668          * Fire before select file
26669          * @param {Roo.bootstrap.DocumentManager} this
26670          */
26671         "beforeselectfile" : true,
26672         /**
26673          * @event process
26674          * Fire before process file
26675          * @param {Roo.bootstrap.DocumentManager} this
26676          * @param {Object} file
26677          */
26678         "process" : true
26679         
26680     });
26681 };
26682
26683 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26684     
26685     boxes : 0,
26686     inputName : '',
26687     thumbSize : 300,
26688     multiple : true,
26689     files : [],
26690     method : 'POST',
26691     url : '',
26692     paramName : 'imageUpload',
26693     fieldLabel : '',
26694     labelWidth : 4,
26695     labelAlign : 'left',
26696     editable : true,
26697     delegates : [],
26698     
26699     
26700     xhr : false, 
26701     
26702     getAutoCreate : function()
26703     {   
26704         var managerWidget = {
26705             tag : 'div',
26706             cls : 'roo-document-manager',
26707             cn : [
26708                 {
26709                     tag : 'input',
26710                     cls : 'roo-document-manager-selector',
26711                     type : 'file'
26712                 },
26713                 {
26714                     tag : 'div',
26715                     cls : 'roo-document-manager-uploader',
26716                     cn : [
26717                         {
26718                             tag : 'div',
26719                             cls : 'roo-document-manager-upload-btn',
26720                             html : '<i class="fa fa-plus"></i>'
26721                         }
26722                     ]
26723                     
26724                 }
26725             ]
26726         };
26727         
26728         var content = [
26729             {
26730                 tag : 'div',
26731                 cls : 'column col-md-12',
26732                 cn : managerWidget
26733             }
26734         ];
26735         
26736         if(this.fieldLabel.length){
26737             
26738             content = [
26739                 {
26740                     tag : 'div',
26741                     cls : 'column col-md-12',
26742                     html : this.fieldLabel
26743                 },
26744                 {
26745                     tag : 'div',
26746                     cls : 'column col-md-12',
26747                     cn : managerWidget
26748                 }
26749             ];
26750
26751             if(this.labelAlign == 'left'){
26752                 content = [
26753                     {
26754                         tag : 'div',
26755                         cls : 'column col-md-' + this.labelWidth,
26756                         html : this.fieldLabel
26757                     },
26758                     {
26759                         tag : 'div',
26760                         cls : 'column col-md-' + (12 - this.labelWidth),
26761                         cn : managerWidget
26762                     }
26763                 ];
26764                 
26765             }
26766         }
26767         
26768         var cfg = {
26769             tag : 'div',
26770             cls : 'row clearfix',
26771             cn : content
26772         };
26773         
26774         return cfg;
26775         
26776     },
26777     
26778     initEvents : function()
26779     {
26780         this.managerEl = this.el.select('.roo-document-manager', true).first();
26781         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26782         
26783         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26784         this.selectorEl.hide();
26785         
26786         if(this.multiple){
26787             this.selectorEl.attr('multiple', 'multiple');
26788         }
26789         
26790         this.selectorEl.on('change', this.onFileSelected, this);
26791         
26792         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26793         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26794         
26795         this.uploader.on('click', this.onUploaderClick, this);
26796         
26797         this.renderProgressDialog();
26798         
26799         var _this = this;
26800         
26801         window.addEventListener("resize", function() { _this.refresh(); } );
26802         
26803         this.fireEvent('initial', this);
26804     },
26805     
26806     renderProgressDialog : function()
26807     {
26808         var _this = this;
26809         
26810         this.progressDialog = new Roo.bootstrap.Modal({
26811             cls : 'roo-document-manager-progress-dialog',
26812             allow_close : false,
26813             title : '',
26814             buttons : [
26815                 {
26816                     name  :'cancel',
26817                     weight : 'danger',
26818                     html : 'Cancel'
26819                 }
26820             ], 
26821             listeners : { 
26822                 btnclick : function() {
26823                     _this.uploadCancel();
26824                     this.hide();
26825                 }
26826             }
26827         });
26828          
26829         this.progressDialog.render(Roo.get(document.body));
26830          
26831         this.progress = new Roo.bootstrap.Progress({
26832             cls : 'roo-document-manager-progress',
26833             active : true,
26834             striped : true
26835         });
26836         
26837         this.progress.render(this.progressDialog.getChildContainer());
26838         
26839         this.progressBar = new Roo.bootstrap.ProgressBar({
26840             cls : 'roo-document-manager-progress-bar',
26841             aria_valuenow : 0,
26842             aria_valuemin : 0,
26843             aria_valuemax : 12,
26844             panel : 'success'
26845         });
26846         
26847         this.progressBar.render(this.progress.getChildContainer());
26848     },
26849     
26850     onUploaderClick : function(e)
26851     {
26852         e.preventDefault();
26853      
26854         if(this.fireEvent('beforeselectfile', this) != false){
26855             this.selectorEl.dom.click();
26856         }
26857         
26858     },
26859     
26860     onFileSelected : function(e)
26861     {
26862         e.preventDefault();
26863         
26864         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26865             return;
26866         }
26867         
26868         Roo.each(this.selectorEl.dom.files, function(file){
26869             if(this.fireEvent('inspect', this, file) != false){
26870                 this.files.push(file);
26871             }
26872         }, this);
26873         
26874         this.queue();
26875         
26876     },
26877     
26878     queue : function()
26879     {
26880         this.selectorEl.dom.value = '';
26881         
26882         if(!this.files.length){
26883             return;
26884         }
26885         
26886         if(this.boxes > 0 && this.files.length > this.boxes){
26887             this.files = this.files.slice(0, this.boxes);
26888         }
26889         
26890         this.uploader.show();
26891         
26892         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26893             this.uploader.hide();
26894         }
26895         
26896         var _this = this;
26897         
26898         var files = [];
26899         
26900         var docs = [];
26901         
26902         Roo.each(this.files, function(file){
26903             
26904             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26905                 var f = this.renderPreview(file);
26906                 files.push(f);
26907                 return;
26908             }
26909             
26910             if(file.type.indexOf('image') != -1){
26911                 this.delegates.push(
26912                     (function(){
26913                         _this.process(file);
26914                     }).createDelegate(this)
26915                 );
26916         
26917                 return;
26918             }
26919             
26920             docs.push(
26921                 (function(){
26922                     _this.process(file);
26923                 }).createDelegate(this)
26924             );
26925             
26926         }, this);
26927         
26928         this.files = files;
26929         
26930         this.delegates = this.delegates.concat(docs);
26931         
26932         if(!this.delegates.length){
26933             this.refresh();
26934             return;
26935         }
26936         
26937         this.progressBar.aria_valuemax = this.delegates.length;
26938         
26939         this.arrange();
26940         
26941         return;
26942     },
26943     
26944     arrange : function()
26945     {
26946         if(!this.delegates.length){
26947             this.progressDialog.hide();
26948             this.refresh();
26949             return;
26950         }
26951         
26952         var delegate = this.delegates.shift();
26953         
26954         this.progressDialog.show();
26955         
26956         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26957         
26958         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26959         
26960         delegate();
26961     },
26962     
26963     refresh : function()
26964     {
26965         this.uploader.show();
26966         
26967         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26968             this.uploader.hide();
26969         }
26970         
26971         Roo.isTouch ? this.closable(false) : this.closable(true);
26972         
26973         this.fireEvent('refresh', this);
26974     },
26975     
26976     onRemove : function(e, el, o)
26977     {
26978         e.preventDefault();
26979         
26980         this.fireEvent('remove', this, o);
26981         
26982     },
26983     
26984     remove : function(o)
26985     {
26986         var files = [];
26987         
26988         Roo.each(this.files, function(file){
26989             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26990                 files.push(file);
26991                 return;
26992             }
26993
26994             o.target.remove();
26995
26996         }, this);
26997         
26998         this.files = files;
26999         
27000         this.refresh();
27001     },
27002     
27003     clear : function()
27004     {
27005         Roo.each(this.files, function(file){
27006             if(!file.target){
27007                 return;
27008             }
27009             
27010             file.target.remove();
27011
27012         }, this);
27013         
27014         this.files = [];
27015         
27016         this.refresh();
27017     },
27018     
27019     onClick : function(e, el, o)
27020     {
27021         e.preventDefault();
27022         
27023         this.fireEvent('click', this, o);
27024         
27025     },
27026     
27027     closable : function(closable)
27028     {
27029         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27030             
27031             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27032             
27033             if(closable){
27034                 el.show();
27035                 return;
27036             }
27037             
27038             el.hide();
27039             
27040         }, this);
27041     },
27042     
27043     xhrOnLoad : function(xhr)
27044     {
27045         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27046             el.remove();
27047         }, this);
27048         
27049         if (xhr.readyState !== 4) {
27050             this.arrange();
27051             this.fireEvent('exception', this, xhr);
27052             return;
27053         }
27054
27055         var response = Roo.decode(xhr.responseText);
27056         
27057         if(!response.success){
27058             this.arrange();
27059             this.fireEvent('exception', this, xhr);
27060             return;
27061         }
27062         
27063         var file = this.renderPreview(response.data);
27064         
27065         this.files.push(file);
27066         
27067         this.arrange();
27068         
27069     },
27070     
27071     xhrOnError : function(xhr)
27072     {
27073         Roo.log('xhr on error');
27074         
27075         var response = Roo.decode(xhr.responseText);
27076           
27077         Roo.log(response);
27078         
27079         this.arrange();
27080     },
27081     
27082     process : function(file)
27083     {
27084         if(this.fireEvent('process', this, file) !== false){
27085             if(this.editable && file.type.indexOf('image') != -1){
27086                 this.fireEvent('edit', this, file);
27087                 return;
27088             }
27089
27090             this.uploadStart(file, false);
27091
27092             return;
27093         }
27094         
27095     },
27096     
27097     uploadStart : function(file, crop)
27098     {
27099         this.xhr = new XMLHttpRequest();
27100         
27101         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27102             this.arrange();
27103             return;
27104         }
27105         
27106         file.xhr = this.xhr;
27107             
27108         this.managerEl.createChild({
27109             tag : 'div',
27110             cls : 'roo-document-manager-loading',
27111             cn : [
27112                 {
27113                     tag : 'div',
27114                     tooltip : file.name,
27115                     cls : 'roo-document-manager-thumb',
27116                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27117                 }
27118             ]
27119
27120         });
27121
27122         this.xhr.open(this.method, this.url, true);
27123         
27124         var headers = {
27125             "Accept": "application/json",
27126             "Cache-Control": "no-cache",
27127             "X-Requested-With": "XMLHttpRequest"
27128         };
27129         
27130         for (var headerName in headers) {
27131             var headerValue = headers[headerName];
27132             if (headerValue) {
27133                 this.xhr.setRequestHeader(headerName, headerValue);
27134             }
27135         }
27136         
27137         var _this = this;
27138         
27139         this.xhr.onload = function()
27140         {
27141             _this.xhrOnLoad(_this.xhr);
27142         }
27143         
27144         this.xhr.onerror = function()
27145         {
27146             _this.xhrOnError(_this.xhr);
27147         }
27148         
27149         var formData = new FormData();
27150
27151         formData.append('returnHTML', 'NO');
27152         
27153         if(crop){
27154             formData.append('crop', crop);
27155         }
27156         
27157         formData.append(this.paramName, file, file.name);
27158         
27159         if(this.fireEvent('prepare', this, formData) != false){
27160             this.xhr.send(formData);
27161         };
27162     },
27163     
27164     uploadCancel : function()
27165     {
27166         if (this.xhr) {
27167             this.xhr.abort();
27168         }
27169         
27170         
27171         this.delegates = [];
27172         
27173         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27174             el.remove();
27175         }, this);
27176         
27177         this.arrange();
27178     },
27179     
27180     renderPreview : function(file)
27181     {
27182         if(typeof(file.target) != 'undefined' && file.target){
27183             return file;
27184         }
27185         
27186         var previewEl = this.managerEl.createChild({
27187             tag : 'div',
27188             cls : 'roo-document-manager-preview',
27189             cn : [
27190                 {
27191                     tag : 'div',
27192                     tooltip : file.filename,
27193                     cls : 'roo-document-manager-thumb',
27194                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27195                 },
27196                 {
27197                     tag : 'button',
27198                     cls : 'close',
27199                     html : '<i class="fa fa-times-circle"></i>'
27200                 }
27201             ]
27202         });
27203
27204         var close = previewEl.select('button.close', true).first();
27205
27206         close.on('click', this.onRemove, this, file);
27207
27208         file.target = previewEl;
27209
27210         var image = previewEl.select('img', true).first();
27211         
27212         var _this = this;
27213         
27214         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27215         
27216         image.on('click', this.onClick, this, file);
27217         
27218         return file;
27219         
27220     },
27221     
27222     onPreviewLoad : function(file, image)
27223     {
27224         if(typeof(file.target) == 'undefined' || !file.target){
27225             return;
27226         }
27227         
27228         var width = image.dom.naturalWidth || image.dom.width;
27229         var height = image.dom.naturalHeight || image.dom.height;
27230         
27231         if(width > height){
27232             file.target.addClass('wide');
27233             return;
27234         }
27235         
27236         file.target.addClass('tall');
27237         return;
27238         
27239     },
27240     
27241     uploadFromSource : function(file, crop)
27242     {
27243         this.xhr = new XMLHttpRequest();
27244         
27245         this.managerEl.createChild({
27246             tag : 'div',
27247             cls : 'roo-document-manager-loading',
27248             cn : [
27249                 {
27250                     tag : 'div',
27251                     tooltip : file.name,
27252                     cls : 'roo-document-manager-thumb',
27253                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27254                 }
27255             ]
27256
27257         });
27258
27259         this.xhr.open(this.method, this.url, true);
27260         
27261         var headers = {
27262             "Accept": "application/json",
27263             "Cache-Control": "no-cache",
27264             "X-Requested-With": "XMLHttpRequest"
27265         };
27266         
27267         for (var headerName in headers) {
27268             var headerValue = headers[headerName];
27269             if (headerValue) {
27270                 this.xhr.setRequestHeader(headerName, headerValue);
27271             }
27272         }
27273         
27274         var _this = this;
27275         
27276         this.xhr.onload = function()
27277         {
27278             _this.xhrOnLoad(_this.xhr);
27279         }
27280         
27281         this.xhr.onerror = function()
27282         {
27283             _this.xhrOnError(_this.xhr);
27284         }
27285         
27286         var formData = new FormData();
27287
27288         formData.append('returnHTML', 'NO');
27289         
27290         formData.append('crop', crop);
27291         
27292         if(typeof(file.filename) != 'undefined'){
27293             formData.append('filename', file.filename);
27294         }
27295         
27296         if(typeof(file.mimetype) != 'undefined'){
27297             formData.append('mimetype', file.mimetype);
27298         }
27299         
27300         if(this.fireEvent('prepare', this, formData) != false){
27301             this.xhr.send(formData);
27302         };
27303     }
27304 });
27305
27306 /*
27307 * Licence: LGPL
27308 */
27309
27310 /**
27311  * @class Roo.bootstrap.DocumentViewer
27312  * @extends Roo.bootstrap.Component
27313  * Bootstrap DocumentViewer class
27314  * 
27315  * @constructor
27316  * Create a new DocumentViewer
27317  * @param {Object} config The config object
27318  */
27319
27320 Roo.bootstrap.DocumentViewer = function(config){
27321     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27322     
27323     this.addEvents({
27324         /**
27325          * @event initial
27326          * Fire after initEvent
27327          * @param {Roo.bootstrap.DocumentViewer} this
27328          */
27329         "initial" : true,
27330         /**
27331          * @event click
27332          * Fire after click
27333          * @param {Roo.bootstrap.DocumentViewer} this
27334          */
27335         "click" : true,
27336         /**
27337          * @event trash
27338          * Fire after trash button
27339          * @param {Roo.bootstrap.DocumentViewer} this
27340          */
27341         "trash" : true
27342         
27343     });
27344 };
27345
27346 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27347     
27348     getAutoCreate : function()
27349     {
27350         var cfg = {
27351             tag : 'div',
27352             cls : 'roo-document-viewer',
27353             cn : [
27354                 {
27355                     tag : 'div',
27356                     cls : 'roo-document-viewer-body',
27357                     cn : [
27358                         {
27359                             tag : 'div',
27360                             cls : 'roo-document-viewer-thumb',
27361                             cn : [
27362                                 {
27363                                     tag : 'img',
27364                                     cls : 'roo-document-viewer-image'
27365                                 }
27366                             ]
27367                         }
27368                     ]
27369                 },
27370                 {
27371                     tag : 'div',
27372                     cls : 'roo-document-viewer-footer',
27373                     cn : {
27374                         tag : 'div',
27375                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27376                         cn : [
27377                             {
27378                                 tag : 'div',
27379                                 cls : 'btn-group',
27380                                 cn : [
27381                                     {
27382                                         tag : 'button',
27383                                         cls : 'btn btn-default roo-document-viewer-trash',
27384                                         html : '<i class="fa fa-trash"></i>'
27385                                     }
27386                                 ]
27387                             }
27388                         ]
27389                     }
27390                 }
27391             ]
27392         };
27393         
27394         return cfg;
27395     },
27396     
27397     initEvents : function()
27398     {
27399         
27400         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27401         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27402         
27403         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27404         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27405         
27406         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27407         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27408         
27409         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27410         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27411         
27412         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27413         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27414         
27415         this.bodyEl.on('click', this.onClick, this);
27416         
27417         this.trashBtn.on('click', this.onTrash, this);
27418         
27419     },
27420     
27421     initial : function()
27422     {
27423 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27424         
27425         
27426         this.fireEvent('initial', this);
27427         
27428     },
27429     
27430     onClick : function(e)
27431     {
27432         e.preventDefault();
27433         
27434         this.fireEvent('click', this);
27435     },
27436     
27437     onTrash : function(e)
27438     {
27439         e.preventDefault();
27440         
27441         this.fireEvent('trash', this);
27442     }
27443     
27444 });
27445 /*
27446  * - LGPL
27447  *
27448  * nav progress bar
27449  * 
27450  */
27451
27452 /**
27453  * @class Roo.bootstrap.NavProgressBar
27454  * @extends Roo.bootstrap.Component
27455  * Bootstrap NavProgressBar class
27456  * 
27457  * @constructor
27458  * Create a new nav progress bar
27459  * @param {Object} config The config object
27460  */
27461
27462 Roo.bootstrap.NavProgressBar = function(config){
27463     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27464
27465     this.bullets = this.bullets || [];
27466    
27467 //    Roo.bootstrap.NavProgressBar.register(this);
27468      this.addEvents({
27469         /**
27470              * @event changed
27471              * Fires when the active item changes
27472              * @param {Roo.bootstrap.NavProgressBar} this
27473              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27474              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27475          */
27476         'changed': true
27477      });
27478     
27479 };
27480
27481 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27482     
27483     bullets : [],
27484     barItems : [],
27485     
27486     getAutoCreate : function()
27487     {
27488         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27489         
27490         cfg = {
27491             tag : 'div',
27492             cls : 'roo-navigation-bar-group',
27493             cn : [
27494                 {
27495                     tag : 'div',
27496                     cls : 'roo-navigation-top-bar'
27497                 },
27498                 {
27499                     tag : 'div',
27500                     cls : 'roo-navigation-bullets-bar',
27501                     cn : [
27502                         {
27503                             tag : 'ul',
27504                             cls : 'roo-navigation-bar'
27505                         }
27506                     ]
27507                 },
27508                 
27509                 {
27510                     tag : 'div',
27511                     cls : 'roo-navigation-bottom-bar'
27512                 }
27513             ]
27514             
27515         };
27516         
27517         return cfg;
27518         
27519     },
27520     
27521     initEvents: function() 
27522     {
27523         
27524     },
27525     
27526     onRender : function(ct, position) 
27527     {
27528         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27529         
27530         if(this.bullets.length){
27531             Roo.each(this.bullets, function(b){
27532                this.addItem(b);
27533             }, this);
27534         }
27535         
27536         this.format();
27537         
27538     },
27539     
27540     addItem : function(cfg)
27541     {
27542         var item = new Roo.bootstrap.NavProgressItem(cfg);
27543         
27544         item.parentId = this.id;
27545         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27546         
27547         if(cfg.html){
27548             var top = new Roo.bootstrap.Element({
27549                 tag : 'div',
27550                 cls : 'roo-navigation-bar-text'
27551             });
27552             
27553             var bottom = new Roo.bootstrap.Element({
27554                 tag : 'div',
27555                 cls : 'roo-navigation-bar-text'
27556             });
27557             
27558             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27559             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27560             
27561             var topText = new Roo.bootstrap.Element({
27562                 tag : 'span',
27563                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27564             });
27565             
27566             var bottomText = new Roo.bootstrap.Element({
27567                 tag : 'span',
27568                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27569             });
27570             
27571             topText.onRender(top.el, null);
27572             bottomText.onRender(bottom.el, null);
27573             
27574             item.topEl = top;
27575             item.bottomEl = bottom;
27576         }
27577         
27578         this.barItems.push(item);
27579         
27580         return item;
27581     },
27582     
27583     getActive : function()
27584     {
27585         var active = false;
27586         
27587         Roo.each(this.barItems, function(v){
27588             
27589             if (!v.isActive()) {
27590                 return;
27591             }
27592             
27593             active = v;
27594             return false;
27595             
27596         });
27597         
27598         return active;
27599     },
27600     
27601     setActiveItem : function(item)
27602     {
27603         var prev = false;
27604         
27605         Roo.each(this.barItems, function(v){
27606             if (v.rid == item.rid) {
27607                 return ;
27608             }
27609             
27610             if (v.isActive()) {
27611                 v.setActive(false);
27612                 prev = v;
27613             }
27614         });
27615
27616         item.setActive(true);
27617         
27618         this.fireEvent('changed', this, item, prev);
27619     },
27620     
27621     getBarItem: function(rid)
27622     {
27623         var ret = false;
27624         
27625         Roo.each(this.barItems, function(e) {
27626             if (e.rid != rid) {
27627                 return;
27628             }
27629             
27630             ret =  e;
27631             return false;
27632         });
27633         
27634         return ret;
27635     },
27636     
27637     indexOfItem : function(item)
27638     {
27639         var index = false;
27640         
27641         Roo.each(this.barItems, function(v, i){
27642             
27643             if (v.rid != item.rid) {
27644                 return;
27645             }
27646             
27647             index = i;
27648             return false
27649         });
27650         
27651         return index;
27652     },
27653     
27654     setActiveNext : function()
27655     {
27656         var i = this.indexOfItem(this.getActive());
27657         
27658         if (i > this.barItems.length) {
27659             return;
27660         }
27661         
27662         this.setActiveItem(this.barItems[i+1]);
27663     },
27664     
27665     setActivePrev : function()
27666     {
27667         var i = this.indexOfItem(this.getActive());
27668         
27669         if (i  < 1) {
27670             return;
27671         }
27672         
27673         this.setActiveItem(this.barItems[i-1]);
27674     },
27675     
27676     format : function()
27677     {
27678         if(!this.barItems.length){
27679             return;
27680         }
27681      
27682         var width = 100 / this.barItems.length;
27683         
27684         Roo.each(this.barItems, function(i){
27685             i.el.setStyle('width', width + '%');
27686             i.topEl.el.setStyle('width', width + '%');
27687             i.bottomEl.el.setStyle('width', width + '%');
27688         }, this);
27689         
27690     }
27691     
27692 });
27693 /*
27694  * - LGPL
27695  *
27696  * Nav Progress Item
27697  * 
27698  */
27699
27700 /**
27701  * @class Roo.bootstrap.NavProgressItem
27702  * @extends Roo.bootstrap.Component
27703  * Bootstrap NavProgressItem class
27704  * @cfg {String} rid the reference id
27705  * @cfg {Boolean} active (true|false) Is item active default false
27706  * @cfg {Boolean} disabled (true|false) Is item active default false
27707  * @cfg {String} html
27708  * @cfg {String} position (top|bottom) text position default bottom
27709  * @cfg {String} icon show icon instead of number
27710  * 
27711  * @constructor
27712  * Create a new NavProgressItem
27713  * @param {Object} config The config object
27714  */
27715 Roo.bootstrap.NavProgressItem = function(config){
27716     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27717     this.addEvents({
27718         // raw events
27719         /**
27720          * @event click
27721          * The raw click event for the entire grid.
27722          * @param {Roo.bootstrap.NavProgressItem} this
27723          * @param {Roo.EventObject} e
27724          */
27725         "click" : true
27726     });
27727    
27728 };
27729
27730 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27731     
27732     rid : '',
27733     active : false,
27734     disabled : false,
27735     html : '',
27736     position : 'bottom',
27737     icon : false,
27738     
27739     getAutoCreate : function()
27740     {
27741         var iconCls = 'roo-navigation-bar-item-icon';
27742         
27743         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27744         
27745         var cfg = {
27746             tag: 'li',
27747             cls: 'roo-navigation-bar-item',
27748             cn : [
27749                 {
27750                     tag : 'i',
27751                     cls : iconCls
27752                 }
27753             ]
27754         };
27755         
27756         if(this.active){
27757             cfg.cls += ' active';
27758         }
27759         if(this.disabled){
27760             cfg.cls += ' disabled';
27761         }
27762         
27763         return cfg;
27764     },
27765     
27766     disable : function()
27767     {
27768         this.setDisabled(true);
27769     },
27770     
27771     enable : function()
27772     {
27773         this.setDisabled(false);
27774     },
27775     
27776     initEvents: function() 
27777     {
27778         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27779         
27780         this.iconEl.on('click', this.onClick, this);
27781     },
27782     
27783     onClick : function(e)
27784     {
27785         e.preventDefault();
27786         
27787         if(this.disabled){
27788             return;
27789         }
27790         
27791         if(this.fireEvent('click', this, e) === false){
27792             return;
27793         };
27794         
27795         this.parent().setActiveItem(this);
27796     },
27797     
27798     isActive: function () 
27799     {
27800         return this.active;
27801     },
27802     
27803     setActive : function(state)
27804     {
27805         if(this.active == state){
27806             return;
27807         }
27808         
27809         this.active = state;
27810         
27811         if (state) {
27812             this.el.addClass('active');
27813             return;
27814         }
27815         
27816         this.el.removeClass('active');
27817         
27818         return;
27819     },
27820     
27821     setDisabled : function(state)
27822     {
27823         if(this.disabled == state){
27824             return;
27825         }
27826         
27827         this.disabled = state;
27828         
27829         if (state) {
27830             this.el.addClass('disabled');
27831             return;
27832         }
27833         
27834         this.el.removeClass('disabled');
27835     },
27836     
27837     tooltipEl : function()
27838     {
27839         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27840     }
27841 });
27842  
27843
27844  /*
27845  * - LGPL
27846  *
27847  * FieldLabel
27848  * 
27849  */
27850
27851 /**
27852  * @class Roo.bootstrap.FieldLabel
27853  * @extends Roo.bootstrap.Component
27854  * Bootstrap FieldLabel class
27855  * @cfg {String} html contents of the element
27856  * @cfg {String} tag tag of the element default label
27857  * @cfg {String} cls class of the element
27858  * @cfg {String} target label target 
27859  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27860  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27861  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27862  * @cfg {String} iconTooltip default "This field is required"
27863  * 
27864  * @constructor
27865  * Create a new FieldLabel
27866  * @param {Object} config The config object
27867  */
27868
27869 Roo.bootstrap.FieldLabel = function(config){
27870     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27871     
27872     this.addEvents({
27873             /**
27874              * @event invalid
27875              * Fires after the field has been marked as invalid.
27876              * @param {Roo.form.FieldLabel} this
27877              * @param {String} msg The validation message
27878              */
27879             invalid : true,
27880             /**
27881              * @event valid
27882              * Fires after the field has been validated with no errors.
27883              * @param {Roo.form.FieldLabel} this
27884              */
27885             valid : true
27886         });
27887 };
27888
27889 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27890     
27891     tag: 'label',
27892     cls: '',
27893     html: '',
27894     target: '',
27895     allowBlank : true,
27896     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27897     validClass : 'text-success fa fa-lg fa-check',
27898     iconTooltip : 'This field is required',
27899     
27900     getAutoCreate : function(){
27901         
27902         var cfg = {
27903             tag : this.tag,
27904             cls : 'roo-bootstrap-field-label ' + this.cls,
27905             for : this.target,
27906             cn : [
27907                 {
27908                     tag : 'i',
27909                     cls : '',
27910                     tooltip : this.iconTooltip
27911                 },
27912                 {
27913                     tag : 'span',
27914                     html : this.html
27915                 }
27916             ] 
27917         };
27918         
27919         return cfg;
27920     },
27921     
27922     initEvents: function() 
27923     {
27924         Roo.bootstrap.Element.superclass.initEvents.call(this);
27925         
27926         this.iconEl = this.el.select('i', true).first();
27927         
27928         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27929         
27930         Roo.bootstrap.FieldLabel.register(this);
27931     },
27932     
27933     /**
27934      * Mark this field as valid
27935      */
27936     markValid : function()
27937     {
27938         this.iconEl.show();
27939         
27940         this.iconEl.removeClass(this.invalidClass);
27941         
27942         this.iconEl.addClass(this.validClass);
27943         
27944         this.fireEvent('valid', this);
27945     },
27946     
27947     /**
27948      * Mark this field as invalid
27949      * @param {String} msg The validation message
27950      */
27951     markInvalid : function(msg)
27952     {
27953         this.iconEl.show();
27954         
27955         this.iconEl.removeClass(this.validClass);
27956         
27957         this.iconEl.addClass(this.invalidClass);
27958         
27959         this.fireEvent('invalid', this, msg);
27960     }
27961     
27962    
27963 });
27964
27965 Roo.apply(Roo.bootstrap.FieldLabel, {
27966     
27967     groups: {},
27968     
27969      /**
27970     * register a FieldLabel Group
27971     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27972     */
27973     register : function(label)
27974     {
27975         if(this.groups.hasOwnProperty(label.target)){
27976             return;
27977         }
27978      
27979         this.groups[label.target] = label;
27980         
27981     },
27982     /**
27983     * fetch a FieldLabel Group based on the target
27984     * @param {string} target
27985     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27986     */
27987     get: function(target) {
27988         if (typeof(this.groups[target]) == 'undefined') {
27989             return false;
27990         }
27991         
27992         return this.groups[target] ;
27993     }
27994 });
27995
27996  
27997
27998  /*
27999  * - LGPL
28000  *
28001  * page DateSplitField.
28002  * 
28003  */
28004
28005
28006 /**
28007  * @class Roo.bootstrap.DateSplitField
28008  * @extends Roo.bootstrap.Component
28009  * Bootstrap DateSplitField class
28010  * @cfg {string} fieldLabel - the label associated
28011  * @cfg {Number} labelWidth set the width of label (0-12)
28012  * @cfg {String} labelAlign (top|left)
28013  * @cfg {Boolean} dayAllowBlank (true|false) default false
28014  * @cfg {Boolean} monthAllowBlank (true|false) default false
28015  * @cfg {Boolean} yearAllowBlank (true|false) default false
28016  * @cfg {string} dayPlaceholder 
28017  * @cfg {string} monthPlaceholder
28018  * @cfg {string} yearPlaceholder
28019  * @cfg {string} dayFormat default 'd'
28020  * @cfg {string} monthFormat default 'm'
28021  * @cfg {string} yearFormat default 'Y'
28022
28023  *     
28024  * @constructor
28025  * Create a new DateSplitField
28026  * @param {Object} config The config object
28027  */
28028
28029 Roo.bootstrap.DateSplitField = function(config){
28030     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28031     
28032     this.addEvents({
28033         // raw events
28034          /**
28035          * @event years
28036          * getting the data of years
28037          * @param {Roo.bootstrap.DateSplitField} this
28038          * @param {Object} years
28039          */
28040         "years" : true,
28041         /**
28042          * @event days
28043          * getting the data of days
28044          * @param {Roo.bootstrap.DateSplitField} this
28045          * @param {Object} days
28046          */
28047         "days" : true,
28048         /**
28049          * @event invalid
28050          * Fires after the field has been marked as invalid.
28051          * @param {Roo.form.Field} this
28052          * @param {String} msg The validation message
28053          */
28054         invalid : true,
28055        /**
28056          * @event valid
28057          * Fires after the field has been validated with no errors.
28058          * @param {Roo.form.Field} this
28059          */
28060         valid : true
28061     });
28062 };
28063
28064 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28065     
28066     fieldLabel : '',
28067     labelAlign : 'top',
28068     labelWidth : 3,
28069     dayAllowBlank : false,
28070     monthAllowBlank : false,
28071     yearAllowBlank : false,
28072     dayPlaceholder : '',
28073     monthPlaceholder : '',
28074     yearPlaceholder : '',
28075     dayFormat : 'd',
28076     monthFormat : 'm',
28077     yearFormat : 'Y',
28078     isFormField : true,
28079     
28080     getAutoCreate : function()
28081     {
28082         var cfg = {
28083             tag : 'div',
28084             cls : 'row roo-date-split-field-group',
28085             cn : [
28086                 {
28087                     tag : 'input',
28088                     type : 'hidden',
28089                     cls : 'form-hidden-field roo-date-split-field-group-value',
28090                     name : this.name
28091                 }
28092             ]
28093         };
28094         
28095         if(this.fieldLabel){
28096             cfg.cn.push({
28097                 tag : 'div',
28098                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28099                 cn : [
28100                     {
28101                         tag : 'label',
28102                         html : this.fieldLabel
28103                     }
28104                 ]
28105             });
28106         }
28107         
28108         Roo.each(['day', 'month', 'year'], function(t){
28109             cfg.cn.push({
28110                 tag : 'div',
28111                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28112             });
28113         }, this);
28114         
28115         return cfg;
28116     },
28117     
28118     inputEl: function ()
28119     {
28120         return this.el.select('.roo-date-split-field-group-value', true).first();
28121     },
28122     
28123     onRender : function(ct, position) 
28124     {
28125         var _this = this;
28126         
28127         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28128         
28129         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28130         
28131         this.dayField = new Roo.bootstrap.ComboBox({
28132             allowBlank : this.dayAllowBlank,
28133             alwaysQuery : true,
28134             displayField : 'value',
28135             editable : false,
28136             fieldLabel : '',
28137             forceSelection : true,
28138             mode : 'local',
28139             placeholder : this.dayPlaceholder,
28140             selectOnFocus : true,
28141             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28142             triggerAction : 'all',
28143             typeAhead : true,
28144             valueField : 'value',
28145             store : new Roo.data.SimpleStore({
28146                 data : (function() {    
28147                     var days = [];
28148                     _this.fireEvent('days', _this, days);
28149                     return days;
28150                 })(),
28151                 fields : [ 'value' ]
28152             }),
28153             listeners : {
28154                 select : function (_self, record, index)
28155                 {
28156                     _this.setValue(_this.getValue());
28157                 }
28158             }
28159         });
28160
28161         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28162         
28163         this.monthField = new Roo.bootstrap.MonthField({
28164             after : '<i class=\"fa fa-calendar\"></i>',
28165             allowBlank : this.monthAllowBlank,
28166             placeholder : this.monthPlaceholder,
28167             readOnly : true,
28168             listeners : {
28169                 render : function (_self)
28170                 {
28171                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28172                         e.preventDefault();
28173                         _self.focus();
28174                     });
28175                 },
28176                 select : function (_self, oldvalue, newvalue)
28177                 {
28178                     _this.setValue(_this.getValue());
28179                 }
28180             }
28181         });
28182         
28183         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28184         
28185         this.yearField = new Roo.bootstrap.ComboBox({
28186             allowBlank : this.yearAllowBlank,
28187             alwaysQuery : true,
28188             displayField : 'value',
28189             editable : false,
28190             fieldLabel : '',
28191             forceSelection : true,
28192             mode : 'local',
28193             placeholder : this.yearPlaceholder,
28194             selectOnFocus : true,
28195             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28196             triggerAction : 'all',
28197             typeAhead : true,
28198             valueField : 'value',
28199             store : new Roo.data.SimpleStore({
28200                 data : (function() {
28201                     var years = [];
28202                     _this.fireEvent('years', _this, years);
28203                     return years;
28204                 })(),
28205                 fields : [ 'value' ]
28206             }),
28207             listeners : {
28208                 select : function (_self, record, index)
28209                 {
28210                     _this.setValue(_this.getValue());
28211                 }
28212             }
28213         });
28214
28215         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28216     },
28217     
28218     setValue : function(v, format)
28219     {
28220         this.inputEl.dom.value = v;
28221         
28222         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28223         
28224         var d = Date.parseDate(v, f);
28225         
28226         if(!d){
28227             this.validate();
28228             return;
28229         }
28230         
28231         this.setDay(d.format(this.dayFormat));
28232         this.setMonth(d.format(this.monthFormat));
28233         this.setYear(d.format(this.yearFormat));
28234         
28235         this.validate();
28236         
28237         return;
28238     },
28239     
28240     setDay : function(v)
28241     {
28242         this.dayField.setValue(v);
28243         this.inputEl.dom.value = this.getValue();
28244         this.validate();
28245         return;
28246     },
28247     
28248     setMonth : function(v)
28249     {
28250         this.monthField.setValue(v, true);
28251         this.inputEl.dom.value = this.getValue();
28252         this.validate();
28253         return;
28254     },
28255     
28256     setYear : function(v)
28257     {
28258         this.yearField.setValue(v);
28259         this.inputEl.dom.value = this.getValue();
28260         this.validate();
28261         return;
28262     },
28263     
28264     getDay : function()
28265     {
28266         return this.dayField.getValue();
28267     },
28268     
28269     getMonth : function()
28270     {
28271         return this.monthField.getValue();
28272     },
28273     
28274     getYear : function()
28275     {
28276         return this.yearField.getValue();
28277     },
28278     
28279     getValue : function()
28280     {
28281         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28282         
28283         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28284         
28285         return date;
28286     },
28287     
28288     reset : function()
28289     {
28290         this.setDay('');
28291         this.setMonth('');
28292         this.setYear('');
28293         this.inputEl.dom.value = '';
28294         this.validate();
28295         return;
28296     },
28297     
28298     validate : function()
28299     {
28300         var d = this.dayField.validate();
28301         var m = this.monthField.validate();
28302         var y = this.yearField.validate();
28303         
28304         var valid = true;
28305         
28306         if(
28307                 (!this.dayAllowBlank && !d) ||
28308                 (!this.monthAllowBlank && !m) ||
28309                 (!this.yearAllowBlank && !y)
28310         ){
28311             valid = false;
28312         }
28313         
28314         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28315             return valid;
28316         }
28317         
28318         if(valid){
28319             this.markValid();
28320             return valid;
28321         }
28322         
28323         this.markInvalid();
28324         
28325         return valid;
28326     },
28327     
28328     markValid : function()
28329     {
28330         
28331         var label = this.el.select('label', true).first();
28332         var icon = this.el.select('i.fa-star', true).first();
28333
28334         if(label && icon){
28335             icon.remove();
28336         }
28337         
28338         this.fireEvent('valid', this);
28339     },
28340     
28341      /**
28342      * Mark this field as invalid
28343      * @param {String} msg The validation message
28344      */
28345     markInvalid : function(msg)
28346     {
28347         
28348         var label = this.el.select('label', true).first();
28349         var icon = this.el.select('i.fa-star', true).first();
28350
28351         if(label && !icon){
28352             this.el.select('.roo-date-split-field-label', true).createChild({
28353                 tag : 'i',
28354                 cls : 'text-danger fa fa-lg fa-star',
28355                 tooltip : 'This field is required',
28356                 style : 'margin-right:5px;'
28357             }, label, true);
28358         }
28359         
28360         this.fireEvent('invalid', this, msg);
28361     },
28362     
28363     clearInvalid : function()
28364     {
28365         var label = this.el.select('label', true).first();
28366         var icon = this.el.select('i.fa-star', true).first();
28367
28368         if(label && icon){
28369             icon.remove();
28370         }
28371         
28372         this.fireEvent('valid', this);
28373     },
28374     
28375     getName: function()
28376     {
28377         return this.name;
28378     }
28379     
28380 });
28381
28382  /**
28383  *
28384  * This is based on 
28385  * http://masonry.desandro.com
28386  *
28387  * The idea is to render all the bricks based on vertical width...
28388  *
28389  * The original code extends 'outlayer' - we might need to use that....
28390  * 
28391  */
28392
28393
28394 /**
28395  * @class Roo.bootstrap.LayoutMasonry
28396  * @extends Roo.bootstrap.Component
28397  * Bootstrap Layout Masonry class
28398  * 
28399  * @constructor
28400  * Create a new Element
28401  * @param {Object} config The config object
28402  */
28403
28404 Roo.bootstrap.LayoutMasonry = function(config){
28405     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28406     
28407     this.bricks = [];
28408     
28409 };
28410
28411 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28412     
28413     /**
28414      * @cfg {Boolean} isLayoutInstant = no animation?
28415      */   
28416     isLayoutInstant : false, // needed?
28417    
28418     /**
28419      * @cfg {Number} boxWidth  width of the columns
28420      */   
28421     boxWidth : 450,
28422     
28423       /**
28424      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28425      */   
28426     boxHeight : 0,
28427     
28428     /**
28429      * @cfg {Number} padWidth padding below box..
28430      */   
28431     padWidth : 10, 
28432     
28433     /**
28434      * @cfg {Number} gutter gutter width..
28435      */   
28436     gutter : 10, 
28437     
28438     /**
28439      * @cfg {Boolean} isAutoInitial defalut true
28440      */   
28441     isAutoInitial : true, 
28442     
28443     containerWidth: 0,
28444     
28445     /**
28446      * @cfg {Boolean} isHorizontal defalut false
28447      */   
28448     isHorizontal : false, 
28449
28450     currentSize : null,
28451     
28452     tag: 'div',
28453     
28454     cls: '',
28455     
28456     bricks: null, //CompositeElement
28457     
28458     cols : 1,
28459     
28460     _isLayoutInited : false,
28461     
28462 //    isAlternative : false, // only use for vertical layout...
28463     
28464     /**
28465      * @cfg {Number} alternativePadWidth padding below box..
28466      */   
28467     alternativePadWidth : 50, 
28468     
28469     getAutoCreate : function(){
28470         
28471         var cfg = {
28472             tag: this.tag,
28473             cls: 'blog-masonary-wrapper ' + this.cls,
28474             cn : {
28475                 cls : 'mas-boxes masonary'
28476             }
28477         };
28478         
28479         return cfg;
28480     },
28481     
28482     getChildContainer: function( )
28483     {
28484         if (this.boxesEl) {
28485             return this.boxesEl;
28486         }
28487         
28488         this.boxesEl = this.el.select('.mas-boxes').first();
28489         
28490         return this.boxesEl;
28491     },
28492     
28493     
28494     initEvents : function()
28495     {
28496         var _this = this;
28497         
28498         if(this.isAutoInitial){
28499             Roo.log('hook children rendered');
28500             this.on('childrenrendered', function() {
28501                 Roo.log('children rendered');
28502                 _this.initial();
28503             } ,this);
28504         }
28505     },
28506     
28507     initial : function()
28508     {
28509         this.currentSize = this.el.getBox(true);
28510         
28511         Roo.EventManager.onWindowResize(this.resize, this); 
28512
28513         if(!this.isAutoInitial){
28514             this.layout();
28515             return;
28516         }
28517         
28518         this.layout();
28519         
28520         return;
28521         //this.layout.defer(500,this);
28522         
28523     },
28524     
28525     resize : function()
28526     {
28527         Roo.log('resize');
28528         
28529         var cs = this.el.getBox(true);
28530         
28531         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28532             Roo.log("no change in with or X");
28533             return;
28534         }
28535         
28536         this.currentSize = cs;
28537         
28538         this.layout();
28539         
28540     },
28541     
28542     layout : function()
28543     {   
28544         this._resetLayout();
28545         
28546         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28547         
28548         this.layoutItems( isInstant );
28549       
28550         this._isLayoutInited = true;
28551         
28552     },
28553     
28554     _resetLayout : function()
28555     {
28556         if(this.isHorizontal){
28557             this.horizontalMeasureColumns();
28558             return;
28559         }
28560         
28561         this.verticalMeasureColumns();
28562         
28563     },
28564     
28565     verticalMeasureColumns : function()
28566     {
28567         this.getContainerWidth();
28568         
28569 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28570 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28571 //            return;
28572 //        }
28573         
28574         var boxWidth = this.boxWidth + this.padWidth;
28575         
28576         if(this.containerWidth < this.boxWidth){
28577             boxWidth = this.containerWidth
28578         }
28579         
28580         var containerWidth = this.containerWidth;
28581         
28582         var cols = Math.floor(containerWidth / boxWidth);
28583         
28584         this.cols = Math.max( cols, 1 );
28585         
28586         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28587         
28588         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28589         
28590         this.colWidth = boxWidth + avail - this.padWidth;
28591         
28592         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28593         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28594     },
28595     
28596     horizontalMeasureColumns : function()
28597     {
28598         this.getContainerWidth();
28599         
28600         var boxWidth = this.boxWidth;
28601         
28602         if(this.containerWidth < boxWidth){
28603             boxWidth = this.containerWidth;
28604         }
28605         
28606         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28607         
28608         this.el.setHeight(boxWidth);
28609         
28610     },
28611     
28612     getContainerWidth : function()
28613     {
28614         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28615     },
28616     
28617     layoutItems : function( isInstant )
28618     {
28619         var items = Roo.apply([], this.bricks);
28620         
28621         if(this.isHorizontal){
28622             this._horizontalLayoutItems( items , isInstant );
28623             return;
28624         }
28625         
28626 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28627 //            this._verticalAlternativeLayoutItems( items , isInstant );
28628 //            return;
28629 //        }
28630         
28631         this._verticalLayoutItems( items , isInstant );
28632         
28633     },
28634     
28635     _verticalLayoutItems : function ( items , isInstant)
28636     {
28637         if ( !items || !items.length ) {
28638             return;
28639         }
28640         
28641         var standard = [
28642             ['xs', 'xs', 'xs', 'tall'],
28643             ['xs', 'xs', 'tall'],
28644             ['xs', 'xs', 'sm'],
28645             ['xs', 'xs', 'xs'],
28646             ['xs', 'tall'],
28647             ['xs', 'sm'],
28648             ['xs', 'xs'],
28649             ['xs'],
28650             
28651             ['sm', 'xs', 'xs'],
28652             ['sm', 'xs'],
28653             ['sm'],
28654             
28655             ['tall', 'xs', 'xs', 'xs'],
28656             ['tall', 'xs', 'xs'],
28657             ['tall', 'xs'],
28658             ['tall']
28659             
28660         ];
28661         
28662         var queue = [];
28663         
28664         var boxes = [];
28665         
28666         var box = [];
28667         
28668         Roo.each(items, function(item, k){
28669             
28670             switch (item.size) {
28671                 // these layouts take up a full box,
28672                 case 'md' :
28673                 case 'md-left' :
28674                 case 'md-right' :
28675                 case 'wide' :
28676                     
28677                     if(box.length){
28678                         boxes.push(box);
28679                         box = [];
28680                     }
28681                     
28682                     boxes.push([item]);
28683                     
28684                     break;
28685                     
28686                 case 'xs' :
28687                 case 'sm' :
28688                 case 'tall' :
28689                     
28690                     box.push(item);
28691                     
28692                     break;
28693                 default :
28694                     break;
28695                     
28696             }
28697             
28698         }, this);
28699         
28700         if(box.length){
28701             boxes.push(box);
28702             box = [];
28703         }
28704         
28705         var filterPattern = function(box, length)
28706         {
28707             if(!box.length){
28708                 return;
28709             }
28710             
28711             var match = false;
28712             
28713             var pattern = box.slice(0, length);
28714             
28715             var format = [];
28716             
28717             Roo.each(pattern, function(i){
28718                 format.push(i.size);
28719             }, this);
28720             
28721             Roo.each(standard, function(s){
28722                 
28723                 if(String(s) != String(format)){
28724                     return;
28725                 }
28726                 
28727                 match = true;
28728                 return false;
28729                 
28730             }, this);
28731             
28732             if(!match && length == 1){
28733                 return;
28734             }
28735             
28736             if(!match){
28737                 filterPattern(box, length - 1);
28738                 return;
28739             }
28740                 
28741             queue.push(pattern);
28742
28743             box = box.slice(length, box.length);
28744
28745             filterPattern(box, 4);
28746
28747             return;
28748             
28749         }
28750         
28751         Roo.each(boxes, function(box, k){
28752             
28753             if(!box.length){
28754                 return;
28755             }
28756             
28757             if(box.length == 1){
28758                 queue.push(box);
28759                 return;
28760             }
28761             
28762             filterPattern(box, 4);
28763             
28764         }, this);
28765         
28766         this._processVerticalLayoutQueue( queue, isInstant );
28767         
28768     },
28769     
28770 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28771 //    {
28772 //        if ( !items || !items.length ) {
28773 //            return;
28774 //        }
28775 //
28776 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28777 //        
28778 //    },
28779     
28780     _horizontalLayoutItems : function ( items , isInstant)
28781     {
28782         if ( !items || !items.length || items.length < 3) {
28783             return;
28784         }
28785         
28786         items.reverse();
28787         
28788         var eItems = items.slice(0, 3);
28789         
28790         items = items.slice(3, items.length);
28791         
28792         var standard = [
28793             ['xs', 'xs', 'xs', 'wide'],
28794             ['xs', 'xs', 'wide'],
28795             ['xs', 'xs', 'sm'],
28796             ['xs', 'xs', 'xs'],
28797             ['xs', 'wide'],
28798             ['xs', 'sm'],
28799             ['xs', 'xs'],
28800             ['xs'],
28801             
28802             ['sm', 'xs', 'xs'],
28803             ['sm', 'xs'],
28804             ['sm'],
28805             
28806             ['wide', 'xs', 'xs', 'xs'],
28807             ['wide', 'xs', 'xs'],
28808             ['wide', 'xs'],
28809             ['wide'],
28810             
28811             ['wide-thin']
28812         ];
28813         
28814         var queue = [];
28815         
28816         var boxes = [];
28817         
28818         var box = [];
28819         
28820         Roo.each(items, function(item, k){
28821             
28822             switch (item.size) {
28823                 case 'md' :
28824                 case 'md-left' :
28825                 case 'md-right' :
28826                 case 'tall' :
28827                     
28828                     if(box.length){
28829                         boxes.push(box);
28830                         box = [];
28831                     }
28832                     
28833                     boxes.push([item]);
28834                     
28835                     break;
28836                     
28837                 case 'xs' :
28838                 case 'sm' :
28839                 case 'wide' :
28840                 case 'wide-thin' :
28841                     
28842                     box.push(item);
28843                     
28844                     break;
28845                 default :
28846                     break;
28847                     
28848             }
28849             
28850         }, this);
28851         
28852         if(box.length){
28853             boxes.push(box);
28854             box = [];
28855         }
28856         
28857         var filterPattern = function(box, length)
28858         {
28859             if(!box.length){
28860                 return;
28861             }
28862             
28863             var match = false;
28864             
28865             var pattern = box.slice(0, length);
28866             
28867             var format = [];
28868             
28869             Roo.each(pattern, function(i){
28870                 format.push(i.size);
28871             }, this);
28872             
28873             Roo.each(standard, function(s){
28874                 
28875                 if(String(s) != String(format)){
28876                     return;
28877                 }
28878                 
28879                 match = true;
28880                 return false;
28881                 
28882             }, this);
28883             
28884             if(!match && length == 1){
28885                 return;
28886             }
28887             
28888             if(!match){
28889                 filterPattern(box, length - 1);
28890                 return;
28891             }
28892                 
28893             queue.push(pattern);
28894
28895             box = box.slice(length, box.length);
28896
28897             filterPattern(box, 4);
28898
28899             return;
28900             
28901         }
28902         
28903         Roo.each(boxes, function(box, k){
28904             
28905             if(!box.length){
28906                 return;
28907             }
28908             
28909             if(box.length == 1){
28910                 queue.push(box);
28911                 return;
28912             }
28913             
28914             filterPattern(box, 4);
28915             
28916         }, this);
28917         
28918         
28919         var prune = [];
28920         
28921         var pos = this.el.getBox(true);
28922         
28923         var minX = pos.x;
28924         
28925         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28926         
28927         var hit_end = false;
28928         
28929         Roo.each(queue, function(box){
28930             
28931             if(hit_end){
28932                 
28933                 Roo.each(box, function(b){
28934                 
28935                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28936                     b.el.hide();
28937
28938                 }, this);
28939
28940                 return;
28941             }
28942             
28943             var mx = 0;
28944             
28945             Roo.each(box, function(b){
28946                 
28947                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28948                 b.el.show();
28949
28950                 mx = Math.max(mx, b.x);
28951                 
28952             }, this);
28953             
28954             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
28955             
28956             if(maxX < minX){
28957                 
28958                 Roo.each(box, function(b){
28959                 
28960                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28961                     b.el.hide();
28962                     
28963                 }, this);
28964                 
28965                 hit_end = true;
28966                 
28967                 return;
28968             }
28969             
28970             prune.push(box);
28971             
28972         }, this);
28973         
28974         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
28975     },
28976     
28977     /** Sets position of item in DOM
28978     * @param {Element} item
28979     * @param {Number} x - horizontal position
28980     * @param {Number} y - vertical position
28981     * @param {Boolean} isInstant - disables transitions
28982     */
28983     _processVerticalLayoutQueue : function( queue, isInstant )
28984     {
28985         var pos = this.el.getBox(true);
28986         var x = pos.x;
28987         var y = pos.y;
28988         var maxY = [];
28989         
28990         for (var i = 0; i < this.cols; i++){
28991             maxY[i] = pos.y;
28992         }
28993         
28994         Roo.each(queue, function(box, k){
28995             
28996             var col = k % this.cols;
28997             
28998             Roo.each(box, function(b,kk){
28999                 
29000                 b.el.position('absolute');
29001                 
29002                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29003                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29004                 
29005                 if(b.size == 'md-left' || b.size == 'md-right'){
29006                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29007                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29008                 }
29009                 
29010                 b.el.setWidth(width);
29011                 b.el.setHeight(height);
29012                 
29013             }, this);
29014             
29015             for (var i = 0; i < this.cols; i++){
29016                 
29017                 if(maxY[i] < maxY[col]){
29018                     col = i;
29019                     continue;
29020                 }
29021                 
29022                 col = Math.min(col, i);
29023                 
29024             }
29025             
29026             x = pos.x + col * (this.colWidth + this.padWidth);
29027             
29028             y = maxY[col];
29029             
29030             var positions = [];
29031             
29032             switch (box.length){
29033                 case 1 :
29034                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29035                     break;
29036                 case 2 :
29037                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29038                     break;
29039                 case 3 :
29040                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29041                     break;
29042                 case 4 :
29043                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29044                     break;
29045                 default :
29046                     break;
29047             }
29048             
29049             Roo.each(box, function(b,kk){
29050                 
29051                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29052                 
29053                 var sz = b.el.getSize();
29054                 
29055                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29056                 
29057             }, this);
29058             
29059         }, this);
29060         
29061         var mY = 0;
29062         
29063         for (var i = 0; i < this.cols; i++){
29064             mY = Math.max(mY, maxY[i]);
29065         }
29066         
29067         this.el.setHeight(mY - pos.y);
29068         
29069     },
29070     
29071 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29072 //    {
29073 //        var pos = this.el.getBox(true);
29074 //        var x = pos.x;
29075 //        var y = pos.y;
29076 //        var maxX = pos.right;
29077 //        
29078 //        var maxHeight = 0;
29079 //        
29080 //        Roo.each(items, function(item, k){
29081 //            
29082 //            var c = k % 2;
29083 //            
29084 //            item.el.position('absolute');
29085 //                
29086 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29087 //
29088 //            item.el.setWidth(width);
29089 //
29090 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29091 //
29092 //            item.el.setHeight(height);
29093 //            
29094 //            if(c == 0){
29095 //                item.el.setXY([x, y], isInstant ? false : true);
29096 //            } else {
29097 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29098 //            }
29099 //            
29100 //            y = y + height + this.alternativePadWidth;
29101 //            
29102 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29103 //            
29104 //        }, this);
29105 //        
29106 //        this.el.setHeight(maxHeight);
29107 //        
29108 //    },
29109     
29110     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29111     {
29112         var pos = this.el.getBox(true);
29113         
29114         var minX = pos.x;
29115         var minY = pos.y;
29116         
29117         var maxX = pos.right;
29118         
29119         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29120         
29121         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29122         
29123         Roo.each(queue, function(box, k){
29124             
29125             Roo.each(box, function(b, kk){
29126                 
29127                 b.el.position('absolute');
29128                 
29129                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29130                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29131                 
29132                 if(b.size == 'md-left' || b.size == 'md-right'){
29133                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29134                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29135                 }
29136                 
29137                 b.el.setWidth(width);
29138                 b.el.setHeight(height);
29139                 
29140             }, this);
29141             
29142             if(!box.length){
29143                 return;
29144             }
29145             
29146             var positions = [];
29147             
29148             switch (box.length){
29149                 case 1 :
29150                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29151                     break;
29152                 case 2 :
29153                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29154                     break;
29155                 case 3 :
29156                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29157                     break;
29158                 case 4 :
29159                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29160                     break;
29161                 default :
29162                     break;
29163             }
29164             
29165             Roo.each(box, function(b,kk){
29166                 
29167                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29168                 
29169                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29170                 
29171             }, this);
29172             
29173         }, this);
29174         
29175     },
29176     
29177     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29178     {
29179         Roo.each(eItems, function(b,k){
29180             
29181             b.size = (k == 0) ? 'sm' : 'xs';
29182             b.x = (k == 0) ? 2 : 1;
29183             b.y = (k == 0) ? 2 : 1;
29184             
29185             b.el.position('absolute');
29186             
29187             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29188                 
29189             b.el.setWidth(width);
29190             
29191             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29192             
29193             b.el.setHeight(height);
29194             
29195         }, this);
29196
29197         var positions = [];
29198         
29199         positions.push({
29200             x : maxX - this.unitWidth * 2 - this.gutter,
29201             y : minY
29202         });
29203         
29204         positions.push({
29205             x : maxX - this.unitWidth,
29206             y : minY + (this.unitWidth + this.gutter) * 2
29207         });
29208         
29209         positions.push({
29210             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29211             y : minY
29212         });
29213         
29214         Roo.each(eItems, function(b,k){
29215             
29216             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29217
29218         }, this);
29219         
29220     },
29221     
29222     getVerticalOneBoxColPositions : function(x, y, box)
29223     {
29224         var pos = [];
29225         
29226         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29227         
29228         if(box[0].size == 'md-left'){
29229             rand = 0;
29230         }
29231         
29232         if(box[0].size == 'md-right'){
29233             rand = 1;
29234         }
29235         
29236         pos.push({
29237             x : x + (this.unitWidth + this.gutter) * rand,
29238             y : y
29239         });
29240         
29241         return pos;
29242     },
29243     
29244     getVerticalTwoBoxColPositions : function(x, y, box)
29245     {
29246         var pos = [];
29247         
29248         if(box[0].size == 'xs'){
29249             
29250             pos.push({
29251                 x : x,
29252                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29253             });
29254
29255             pos.push({
29256                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29257                 y : y
29258             });
29259             
29260             return pos;
29261             
29262         }
29263         
29264         pos.push({
29265             x : x,
29266             y : y
29267         });
29268
29269         pos.push({
29270             x : x + (this.unitWidth + this.gutter) * 2,
29271             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29272         });
29273         
29274         return pos;
29275         
29276     },
29277     
29278     getVerticalThreeBoxColPositions : function(x, y, box)
29279     {
29280         var pos = [];
29281         
29282         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29283             
29284             pos.push({
29285                 x : x,
29286                 y : y
29287             });
29288
29289             pos.push({
29290                 x : x + (this.unitWidth + this.gutter) * 1,
29291                 y : y
29292             });
29293             
29294             pos.push({
29295                 x : x + (this.unitWidth + this.gutter) * 2,
29296                 y : y
29297             });
29298             
29299             return pos;
29300             
29301         }
29302         
29303         if(box[0].size == 'xs' && box[1].size == 'xs'){
29304             
29305             pos.push({
29306                 x : x,
29307                 y : y
29308             });
29309
29310             pos.push({
29311                 x : x,
29312                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29313             });
29314             
29315             pos.push({
29316                 x : x + (this.unitWidth + this.gutter) * 1,
29317                 y : y
29318             });
29319             
29320             return pos;
29321             
29322         }
29323         
29324         pos.push({
29325             x : x,
29326             y : y
29327         });
29328
29329         pos.push({
29330             x : x + (this.unitWidth + this.gutter) * 2,
29331             y : y
29332         });
29333
29334         pos.push({
29335             x : x + (this.unitWidth + this.gutter) * 2,
29336             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29337         });
29338             
29339         return pos;
29340         
29341     },
29342     
29343     getVerticalFourBoxColPositions : function(x, y, box)
29344     {
29345         var pos = [];
29346         
29347         if(box[0].size == 'xs'){
29348             
29349             pos.push({
29350                 x : x,
29351                 y : y
29352             });
29353
29354             pos.push({
29355                 x : x,
29356                 y : y + (this.unitHeight + this.gutter) * 1
29357             });
29358             
29359             pos.push({
29360                 x : x,
29361                 y : y + (this.unitHeight + this.gutter) * 2
29362             });
29363             
29364             pos.push({
29365                 x : x + (this.unitWidth + this.gutter) * 1,
29366                 y : y
29367             });
29368             
29369             return pos;
29370             
29371         }
29372         
29373         pos.push({
29374             x : x,
29375             y : y
29376         });
29377
29378         pos.push({
29379             x : x + (this.unitWidth + this.gutter) * 2,
29380             y : y
29381         });
29382
29383         pos.push({
29384             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29385             y : y + (this.unitHeight + this.gutter) * 1
29386         });
29387
29388         pos.push({
29389             x : x + (this.unitWidth + this.gutter) * 2,
29390             y : y + (this.unitWidth + this.gutter) * 2
29391         });
29392
29393         return pos;
29394         
29395     },
29396     
29397     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29398     {
29399         var pos = [];
29400         
29401         if(box[0].size == 'md-left'){
29402             pos.push({
29403                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29404                 y : minY
29405             });
29406             
29407             return pos;
29408         }
29409         
29410         if(box[0].size == 'md-right'){
29411             pos.push({
29412                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29413                 y : minY + (this.unitWidth + this.gutter) * 1
29414             });
29415             
29416             return pos;
29417         }
29418         
29419         var rand = Math.floor(Math.random() * (4 - box[0].y));
29420         
29421         pos.push({
29422             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29423             y : minY + (this.unitWidth + this.gutter) * rand
29424         });
29425         
29426         return pos;
29427         
29428     },
29429     
29430     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29431     {
29432         var pos = [];
29433         
29434         if(box[0].size == 'xs'){
29435             
29436             pos.push({
29437                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29438                 y : minY
29439             });
29440
29441             pos.push({
29442                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29443                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29444             });
29445             
29446             return pos;
29447             
29448         }
29449         
29450         pos.push({
29451             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29452             y : minY
29453         });
29454
29455         pos.push({
29456             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29457             y : minY + (this.unitWidth + this.gutter) * 2
29458         });
29459         
29460         return pos;
29461         
29462     },
29463     
29464     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29465     {
29466         var pos = [];
29467         
29468         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29469             
29470             pos.push({
29471                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29472                 y : minY
29473             });
29474
29475             pos.push({
29476                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29477                 y : minY + (this.unitWidth + this.gutter) * 1
29478             });
29479             
29480             pos.push({
29481                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29482                 y : minY + (this.unitWidth + this.gutter) * 2
29483             });
29484             
29485             return pos;
29486             
29487         }
29488         
29489         if(box[0].size == 'xs' && box[1].size == 'xs'){
29490             
29491             pos.push({
29492                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29493                 y : minY
29494             });
29495
29496             pos.push({
29497                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29498                 y : minY
29499             });
29500             
29501             pos.push({
29502                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29503                 y : minY + (this.unitWidth + this.gutter) * 1
29504             });
29505             
29506             return pos;
29507             
29508         }
29509         
29510         pos.push({
29511             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29512             y : minY
29513         });
29514
29515         pos.push({
29516             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29517             y : minY + (this.unitWidth + this.gutter) * 2
29518         });
29519
29520         pos.push({
29521             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29522             y : minY + (this.unitWidth + this.gutter) * 2
29523         });
29524             
29525         return pos;
29526         
29527     },
29528     
29529     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29530     {
29531         var pos = [];
29532         
29533         if(box[0].size == 'xs'){
29534             
29535             pos.push({
29536                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29537                 y : minY
29538             });
29539
29540             pos.push({
29541                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29542                 y : minY
29543             });
29544             
29545             pos.push({
29546                 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),
29547                 y : minY
29548             });
29549             
29550             pos.push({
29551                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29552                 y : minY + (this.unitWidth + this.gutter) * 1
29553             });
29554             
29555             return pos;
29556             
29557         }
29558         
29559         pos.push({
29560             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29561             y : minY
29562         });
29563         
29564         pos.push({
29565             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29566             y : minY + (this.unitWidth + this.gutter) * 2
29567         });
29568         
29569         pos.push({
29570             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29571             y : minY + (this.unitWidth + this.gutter) * 2
29572         });
29573         
29574         pos.push({
29575             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),
29576             y : minY + (this.unitWidth + this.gutter) * 2
29577         });
29578
29579         return pos;
29580         
29581     }
29582     
29583 });
29584
29585  
29586
29587  /**
29588  *
29589  * This is based on 
29590  * http://masonry.desandro.com
29591  *
29592  * The idea is to render all the bricks based on vertical width...
29593  *
29594  * The original code extends 'outlayer' - we might need to use that....
29595  * 
29596  */
29597
29598
29599 /**
29600  * @class Roo.bootstrap.LayoutMasonryAuto
29601  * @extends Roo.bootstrap.Component
29602  * Bootstrap Layout Masonry class
29603  * 
29604  * @constructor
29605  * Create a new Element
29606  * @param {Object} config The config object
29607  */
29608
29609 Roo.bootstrap.LayoutMasonryAuto = function(config){
29610     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29611 };
29612
29613 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29614     
29615       /**
29616      * @cfg {Boolean} isFitWidth  - resize the width..
29617      */   
29618     isFitWidth : false,  // options..
29619     /**
29620      * @cfg {Boolean} isOriginLeft = left align?
29621      */   
29622     isOriginLeft : true,
29623     /**
29624      * @cfg {Boolean} isOriginTop = top align?
29625      */   
29626     isOriginTop : false,
29627     /**
29628      * @cfg {Boolean} isLayoutInstant = no animation?
29629      */   
29630     isLayoutInstant : false, // needed?
29631     /**
29632      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29633      */   
29634     isResizingContainer : true,
29635     /**
29636      * @cfg {Number} columnWidth  width of the columns 
29637      */   
29638     
29639     columnWidth : 0,
29640     /**
29641      * @cfg {Number} padHeight padding below box..
29642      */   
29643     
29644     padHeight : 10, 
29645     
29646     /**
29647      * @cfg {Boolean} isAutoInitial defalut true
29648      */   
29649     
29650     isAutoInitial : true, 
29651     
29652     // private?
29653     gutter : 0,
29654     
29655     containerWidth: 0,
29656     initialColumnWidth : 0,
29657     currentSize : null,
29658     
29659     colYs : null, // array.
29660     maxY : 0,
29661     padWidth: 10,
29662     
29663     
29664     tag: 'div',
29665     cls: '',
29666     bricks: null, //CompositeElement
29667     cols : 0, // array?
29668     // element : null, // wrapped now this.el
29669     _isLayoutInited : null, 
29670     
29671     
29672     getAutoCreate : function(){
29673         
29674         var cfg = {
29675             tag: this.tag,
29676             cls: 'blog-masonary-wrapper ' + this.cls,
29677             cn : {
29678                 cls : 'mas-boxes masonary'
29679             }
29680         };
29681         
29682         return cfg;
29683     },
29684     
29685     getChildContainer: function( )
29686     {
29687         if (this.boxesEl) {
29688             return this.boxesEl;
29689         }
29690         
29691         this.boxesEl = this.el.select('.mas-boxes').first();
29692         
29693         return this.boxesEl;
29694     },
29695     
29696     
29697     initEvents : function()
29698     {
29699         var _this = this;
29700         
29701         if(this.isAutoInitial){
29702             Roo.log('hook children rendered');
29703             this.on('childrenrendered', function() {
29704                 Roo.log('children rendered');
29705                 _this.initial();
29706             } ,this);
29707         }
29708         
29709     },
29710     
29711     initial : function()
29712     {
29713         this.reloadItems();
29714
29715         this.currentSize = this.el.getBox(true);
29716
29717         /// was window resize... - let's see if this works..
29718         Roo.EventManager.onWindowResize(this.resize, this); 
29719
29720         if(!this.isAutoInitial){
29721             this.layout();
29722             return;
29723         }
29724         
29725         this.layout.defer(500,this);
29726     },
29727     
29728     reloadItems: function()
29729     {
29730         this.bricks = this.el.select('.masonry-brick', true);
29731         
29732         this.bricks.each(function(b) {
29733             //Roo.log(b.getSize());
29734             if (!b.attr('originalwidth')) {
29735                 b.attr('originalwidth',  b.getSize().width);
29736             }
29737             
29738         });
29739         
29740         Roo.log(this.bricks.elements.length);
29741     },
29742     
29743     resize : function()
29744     {
29745         Roo.log('resize');
29746         var cs = this.el.getBox(true);
29747         
29748         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29749             Roo.log("no change in with or X");
29750             return;
29751         }
29752         this.currentSize = cs;
29753         this.layout();
29754     },
29755     
29756     layout : function()
29757     {
29758          Roo.log('layout');
29759         this._resetLayout();
29760         //this._manageStamps();
29761       
29762         // don't animate first layout
29763         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29764         this.layoutItems( isInstant );
29765       
29766         // flag for initalized
29767         this._isLayoutInited = true;
29768     },
29769     
29770     layoutItems : function( isInstant )
29771     {
29772         //var items = this._getItemsForLayout( this.items );
29773         // original code supports filtering layout items.. we just ignore it..
29774         
29775         this._layoutItems( this.bricks , isInstant );
29776       
29777         this._postLayout();
29778     },
29779     _layoutItems : function ( items , isInstant)
29780     {
29781        //this.fireEvent( 'layout', this, items );
29782     
29783
29784         if ( !items || !items.elements.length ) {
29785           // no items, emit event with empty array
29786             return;
29787         }
29788
29789         var queue = [];
29790         items.each(function(item) {
29791             Roo.log("layout item");
29792             Roo.log(item);
29793             // get x/y object from method
29794             var position = this._getItemLayoutPosition( item );
29795             // enqueue
29796             position.item = item;
29797             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29798             queue.push( position );
29799         }, this);
29800       
29801         this._processLayoutQueue( queue );
29802     },
29803     /** Sets position of item in DOM
29804     * @param {Element} item
29805     * @param {Number} x - horizontal position
29806     * @param {Number} y - vertical position
29807     * @param {Boolean} isInstant - disables transitions
29808     */
29809     _processLayoutQueue : function( queue )
29810     {
29811         for ( var i=0, len = queue.length; i < len; i++ ) {
29812             var obj = queue[i];
29813             obj.item.position('absolute');
29814             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29815         }
29816     },
29817       
29818     
29819     /**
29820     * Any logic you want to do after each layout,
29821     * i.e. size the container
29822     */
29823     _postLayout : function()
29824     {
29825         this.resizeContainer();
29826     },
29827     
29828     resizeContainer : function()
29829     {
29830         if ( !this.isResizingContainer ) {
29831             return;
29832         }
29833         var size = this._getContainerSize();
29834         if ( size ) {
29835             this.el.setSize(size.width,size.height);
29836             this.boxesEl.setSize(size.width,size.height);
29837         }
29838     },
29839     
29840     
29841     
29842     _resetLayout : function()
29843     {
29844         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29845         this.colWidth = this.el.getWidth();
29846         //this.gutter = this.el.getWidth(); 
29847         
29848         this.measureColumns();
29849
29850         // reset column Y
29851         var i = this.cols;
29852         this.colYs = [];
29853         while (i--) {
29854             this.colYs.push( 0 );
29855         }
29856     
29857         this.maxY = 0;
29858     },
29859
29860     measureColumns : function()
29861     {
29862         this.getContainerWidth();
29863       // if columnWidth is 0, default to outerWidth of first item
29864         if ( !this.columnWidth ) {
29865             var firstItem = this.bricks.first();
29866             Roo.log(firstItem);
29867             this.columnWidth  = this.containerWidth;
29868             if (firstItem && firstItem.attr('originalwidth') ) {
29869                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29870             }
29871             // columnWidth fall back to item of first element
29872             Roo.log("set column width?");
29873                         this.initialColumnWidth = this.columnWidth  ;
29874
29875             // if first elem has no width, default to size of container
29876             
29877         }
29878         
29879         
29880         if (this.initialColumnWidth) {
29881             this.columnWidth = this.initialColumnWidth;
29882         }
29883         
29884         
29885             
29886         // column width is fixed at the top - however if container width get's smaller we should
29887         // reduce it...
29888         
29889         // this bit calcs how man columns..
29890             
29891         var columnWidth = this.columnWidth += this.gutter;
29892       
29893         // calculate columns
29894         var containerWidth = this.containerWidth + this.gutter;
29895         
29896         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29897         // fix rounding errors, typically with gutters
29898         var excess = columnWidth - containerWidth % columnWidth;
29899         
29900         
29901         // if overshoot is less than a pixel, round up, otherwise floor it
29902         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29903         cols = Math[ mathMethod ]( cols );
29904         this.cols = Math.max( cols, 1 );
29905         
29906         
29907          // padding positioning..
29908         var totalColWidth = this.cols * this.columnWidth;
29909         var padavail = this.containerWidth - totalColWidth;
29910         // so for 2 columns - we need 3 'pads'
29911         
29912         var padNeeded = (1+this.cols) * this.padWidth;
29913         
29914         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29915         
29916         this.columnWidth += padExtra
29917         //this.padWidth = Math.floor(padavail /  ( this.cols));
29918         
29919         // adjust colum width so that padding is fixed??
29920         
29921         // we have 3 columns ... total = width * 3
29922         // we have X left over... that should be used by 
29923         
29924         //if (this.expandC) {
29925             
29926         //}
29927         
29928         
29929         
29930     },
29931     
29932     getContainerWidth : function()
29933     {
29934        /* // container is parent if fit width
29935         var container = this.isFitWidth ? this.element.parentNode : this.element;
29936         // check that this.size and size are there
29937         // IE8 triggers resize on body size change, so they might not be
29938         
29939         var size = getSize( container );  //FIXME
29940         this.containerWidth = size && size.innerWidth; //FIXME
29941         */
29942          
29943         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29944         
29945     },
29946     
29947     _getItemLayoutPosition : function( item )  // what is item?
29948     {
29949         // we resize the item to our columnWidth..
29950       
29951         item.setWidth(this.columnWidth);
29952         item.autoBoxAdjust  = false;
29953         
29954         var sz = item.getSize();
29955  
29956         // how many columns does this brick span
29957         var remainder = this.containerWidth % this.columnWidth;
29958         
29959         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
29960         // round if off by 1 pixel, otherwise use ceil
29961         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
29962         colSpan = Math.min( colSpan, this.cols );
29963         
29964         // normally this should be '1' as we dont' currently allow multi width columns..
29965         
29966         var colGroup = this._getColGroup( colSpan );
29967         // get the minimum Y value from the columns
29968         var minimumY = Math.min.apply( Math, colGroup );
29969         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29970         
29971         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
29972          
29973         // position the brick
29974         var position = {
29975             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
29976             y: this.currentSize.y + minimumY + this.padHeight
29977         };
29978         
29979         Roo.log(position);
29980         // apply setHeight to necessary columns
29981         var setHeight = minimumY + sz.height + this.padHeight;
29982         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29983         
29984         var setSpan = this.cols + 1 - colGroup.length;
29985         for ( var i = 0; i < setSpan; i++ ) {
29986           this.colYs[ shortColIndex + i ] = setHeight ;
29987         }
29988       
29989         return position;
29990     },
29991     
29992     /**
29993      * @param {Number} colSpan - number of columns the element spans
29994      * @returns {Array} colGroup
29995      */
29996     _getColGroup : function( colSpan )
29997     {
29998         if ( colSpan < 2 ) {
29999           // if brick spans only one column, use all the column Ys
30000           return this.colYs;
30001         }
30002       
30003         var colGroup = [];
30004         // how many different places could this brick fit horizontally
30005         var groupCount = this.cols + 1 - colSpan;
30006         // for each group potential horizontal position
30007         for ( var i = 0; i < groupCount; i++ ) {
30008           // make an array of colY values for that one group
30009           var groupColYs = this.colYs.slice( i, i + colSpan );
30010           // and get the max value of the array
30011           colGroup[i] = Math.max.apply( Math, groupColYs );
30012         }
30013         return colGroup;
30014     },
30015     /*
30016     _manageStamp : function( stamp )
30017     {
30018         var stampSize =  stamp.getSize();
30019         var offset = stamp.getBox();
30020         // get the columns that this stamp affects
30021         var firstX = this.isOriginLeft ? offset.x : offset.right;
30022         var lastX = firstX + stampSize.width;
30023         var firstCol = Math.floor( firstX / this.columnWidth );
30024         firstCol = Math.max( 0, firstCol );
30025         
30026         var lastCol = Math.floor( lastX / this.columnWidth );
30027         // lastCol should not go over if multiple of columnWidth #425
30028         lastCol -= lastX % this.columnWidth ? 0 : 1;
30029         lastCol = Math.min( this.cols - 1, lastCol );
30030         
30031         // set colYs to bottom of the stamp
30032         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30033             stampSize.height;
30034             
30035         for ( var i = firstCol; i <= lastCol; i++ ) {
30036           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30037         }
30038     },
30039     */
30040     
30041     _getContainerSize : function()
30042     {
30043         this.maxY = Math.max.apply( Math, this.colYs );
30044         var size = {
30045             height: this.maxY
30046         };
30047       
30048         if ( this.isFitWidth ) {
30049             size.width = this._getContainerFitWidth();
30050         }
30051       
30052         return size;
30053     },
30054     
30055     _getContainerFitWidth : function()
30056     {
30057         var unusedCols = 0;
30058         // count unused columns
30059         var i = this.cols;
30060         while ( --i ) {
30061           if ( this.colYs[i] !== 0 ) {
30062             break;
30063           }
30064           unusedCols++;
30065         }
30066         // fit container to columns that have been used
30067         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30068     },
30069     
30070     needsResizeLayout : function()
30071     {
30072         var previousWidth = this.containerWidth;
30073         this.getContainerWidth();
30074         return previousWidth !== this.containerWidth;
30075     }
30076  
30077 });
30078
30079  
30080
30081  /*
30082  * - LGPL
30083  *
30084  * element
30085  * 
30086  */
30087
30088 /**
30089  * @class Roo.bootstrap.MasonryBrick
30090  * @extends Roo.bootstrap.Component
30091  * Bootstrap MasonryBrick class
30092  * 
30093  * @constructor
30094  * Create a new MasonryBrick
30095  * @param {Object} config The config object
30096  */
30097
30098 Roo.bootstrap.MasonryBrick = function(config){
30099     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30100     
30101     this.addEvents({
30102         // raw events
30103         /**
30104          * @event click
30105          * When a MasonryBrick is clcik
30106          * @param {Roo.bootstrap.MasonryBrick} this
30107          * @param {Roo.EventObject} e
30108          */
30109         "click" : true
30110     });
30111 };
30112
30113 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30114     
30115     /**
30116      * @cfg {String} title
30117      */   
30118     title : '',
30119     /**
30120      * @cfg {String} html
30121      */   
30122     html : '',
30123     /**
30124      * @cfg {String} bgimage
30125      */   
30126     bgimage : '',
30127     /**
30128      * @cfg {String} cls
30129      */   
30130     cls : '',
30131     /**
30132      * @cfg {String} href
30133      */   
30134     href : '',
30135     /**
30136      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30137      */   
30138     size : 'xs',
30139     
30140     /**
30141      * @cfg {String} (center|bottom) placetitle
30142      */   
30143     placetitle : '',
30144     
30145     getAutoCreate : function()
30146     {
30147         var cls = 'masonry-brick';
30148         
30149         if(this.href.length){
30150             cls += ' masonry-brick-link';
30151         }
30152         
30153         if(this.bgimage.length){
30154             cls += ' masonry-brick-image';
30155         }
30156         
30157         if(this.size){
30158             cls += ' masonry-' + this.size + '-brick';
30159         }
30160         
30161         if(this.placetitle.length){
30162             
30163             switch (this.placetitle) {
30164                 case 'center' :
30165                     cls += ' masonry-center-title';
30166                     break;
30167                 case 'bottom' :
30168                     cls += ' masonry-bottom-title';
30169                     break;
30170                 default:
30171                     break;
30172             }
30173             
30174         } else {
30175             if(!this.html.length && !this.bgimage.length){
30176                 cls += ' masonry-center-title';
30177             }
30178
30179             if(!this.html.length && this.bgimage.length){
30180                 cls += ' masonry-bottom-title';
30181             }
30182         }
30183         
30184         if(this.cls){
30185             cls += ' ' + this.cls;
30186         }
30187         
30188         var cfg = {
30189             tag: (this.href.length) ? 'a' : 'div',
30190             cls: cls,
30191             cn: [
30192                 {
30193                     tag: 'div',
30194                     cls: 'masonry-brick-paragraph',
30195                     cn: []
30196                 }
30197             ]
30198         };
30199         
30200         if(this.href.length){
30201             cfg.href = this.href;
30202         }
30203         
30204         var cn = cfg.cn[0].cn;
30205         
30206         if(this.title.length){
30207             cn.push({
30208                 tag: 'h4',
30209                 cls: 'masonry-brick-title',
30210                 html: this.title
30211             });
30212         }
30213         
30214         if(this.html.length){
30215             cn.push({
30216                 tag: 'p',
30217                 cls: 'masonry-brick-text',
30218                 html: this.html
30219             });
30220         }
30221         
30222         if(this.bgimage.length){
30223             cfg.cn.push({
30224                 tag: 'img',
30225                 cls: 'masonry-brick-image-view',
30226                 src: this.bgimage
30227             });
30228         }
30229         
30230         return cfg;
30231         
30232     },
30233     
30234     initEvents: function() 
30235     {
30236         switch (this.size) {
30237             case 'xs' :
30238 //                this.intSize = 1;
30239                 this.x = 1;
30240                 this.y = 1;
30241                 break;
30242             case 'sm' :
30243 //                this.intSize = 2;
30244                 this.x = 2;
30245                 this.y = 2;
30246                 break;
30247             case 'md' :
30248             case 'md-left' :
30249             case 'md-right' :
30250 //                this.intSize = 3;
30251                 this.x = 3;
30252                 this.y = 3;
30253                 break;
30254             case 'tall' :
30255 //                this.intSize = 3;
30256                 this.x = 2;
30257                 this.y = 3;
30258                 break;
30259             case 'wide' :
30260 //                this.intSize = 3;
30261                 this.x = 3;
30262                 this.y = 2;
30263                 break;
30264             case 'wide-thin' :
30265 //                this.intSize = 3;
30266                 this.x = 3;
30267                 this.y = 1;
30268                 break;
30269                         
30270             default :
30271                 break;
30272         }
30273         
30274         
30275         
30276         if(Roo.isTouch){
30277             this.el.on('touchstart', this.onTouchStart, this);
30278             this.el.on('touchmove', this.onTouchMove, this);
30279             this.el.on('touchend', this.onTouchEnd, this);
30280         } else {
30281             this.el.on('mouseenter'  ,this.enter, this);
30282             this.el.on('mouseleave', this.leave, this);
30283         }
30284         
30285         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30286             this.parent().bricks.push(this);   
30287         }
30288         
30289     },
30290     
30291     onClick: function(e, el)
30292     {
30293         alert('click');
30294         
30295         if(!Roo.isTouch){
30296             return;
30297         }
30298         
30299         var time = this.endTimer - this.startTimer;
30300         
30301         alert(time);
30302         
30303         if(time < 1000){
30304             return;
30305         }
30306         
30307         e.preventDefault();
30308     },
30309     
30310     enter: function(e, el)
30311     {
30312         e.preventDefault();
30313         
30314         if(this.bgimage.length && this.html.length){
30315             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30316         }
30317     },
30318     
30319     leave: function(e, el)
30320     {
30321         e.preventDefault();
30322         
30323         if(this.bgimage.length && this.html.length){
30324             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30325         }
30326     },
30327     
30328     onTouchStart: function(e, el)
30329     {
30330 //        e.preventDefault();
30331         
30332         if(!this.bgimage.length || !this.html.length){
30333             return;
30334         }
30335         
30336         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30337         
30338         this.timer = new Date().getTime();
30339         
30340         this.touchmoved = false;
30341     },
30342     
30343     onTouchMove: function(e, el)
30344     {
30345         this.touchmoved = true;
30346     },
30347     
30348     onTouchEnd: function(e, el)
30349     {
30350 //        e.preventDefault();
30351         
30352         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30353             return;
30354         }
30355         
30356         if(!this.bgimage.length || !this.html.length){
30357             
30358             if(this.href.length){
30359                 window.location.href = this.href;
30360             }
30361             
30362             return;
30363         }
30364         
30365         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30366         
30367         window.location.href = this.href;
30368     }
30369     
30370 });
30371
30372  
30373
30374  /*
30375  * - LGPL
30376  *
30377  * element
30378  * 
30379  */
30380
30381 /**
30382  * @class Roo.bootstrap.Brick
30383  * @extends Roo.bootstrap.Component
30384  * Bootstrap Brick class
30385  * 
30386  * @constructor
30387  * Create a new Brick
30388  * @param {Object} config The config object
30389  */
30390
30391 Roo.bootstrap.Brick = function(config){
30392     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30393     
30394     this.addEvents({
30395         // raw events
30396         /**
30397          * @event click
30398          * When a Brick is click
30399          * @param {Roo.bootstrap.Brick} this
30400          * @param {Roo.EventObject} e
30401          */
30402         "click" : true
30403     });
30404 };
30405
30406 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30407     
30408     /**
30409      * @cfg {String} title
30410      */   
30411     title : '',
30412     /**
30413      * @cfg {String} html
30414      */   
30415     html : '',
30416     /**
30417      * @cfg {String} bgimage
30418      */   
30419     bgimage : '',
30420     /**
30421      * @cfg {String} cls
30422      */   
30423     cls : '',
30424     /**
30425      * @cfg {String} href
30426      */   
30427     href : '',
30428     /**
30429      * @cfg {String} video
30430      */   
30431     video : '',
30432     /**
30433      * @cfg {Boolean} square
30434      */   
30435     square : true,
30436     
30437     getAutoCreate : function()
30438     {
30439         var cls = 'roo-brick';
30440         
30441         if(this.href.length){
30442             cls += ' roo-brick-link';
30443         }
30444         
30445         if(this.bgimage.length){
30446             cls += ' roo-brick-image';
30447         }
30448         
30449         if(!this.html.length && !this.bgimage.length){
30450             cls += ' roo-brick-center-title';
30451         }
30452         
30453         if(!this.html.length && this.bgimage.length){
30454             cls += ' roo-brick-bottom-title';
30455         }
30456         
30457         if(this.cls){
30458             cls += ' ' + this.cls;
30459         }
30460         
30461         var cfg = {
30462             tag: (this.href.length) ? 'a' : 'div',
30463             cls: cls,
30464             cn: [
30465                 {
30466                     tag: 'div',
30467                     cls: 'roo-brick-paragraph',
30468                     cn: []
30469                 }
30470             ]
30471         };
30472         
30473         if(this.href.length){
30474             cfg.href = this.href;
30475         }
30476         
30477         var cn = cfg.cn[0].cn;
30478         
30479         if(this.title.length){
30480             cn.push({
30481                 tag: 'h4',
30482                 cls: 'roo-brick-title',
30483                 html: this.title
30484             });
30485         }
30486         
30487         if(this.html.length){
30488             cn.push({
30489                 tag: 'p',
30490                 cls: 'roo-brick-text',
30491                 html: this.html
30492             });
30493         }
30494         
30495         if(this.bgimage.length){
30496             cfg.cn.push({
30497                 tag: 'img',
30498                 cls: 'roo-brick-image-view',
30499                 src: this.bgimage
30500             });
30501         }
30502         
30503         return cfg;
30504     },
30505     
30506     initEvents: function() 
30507     {
30508         if(this.title.length || this.html.length){
30509             this.el.on('mouseenter'  ,this.enter, this);
30510             this.el.on('mouseleave', this.leave, this);
30511         }
30512         
30513         
30514         Roo.EventManager.onWindowResize(this.resize, this); 
30515         
30516         this.resize();
30517     },
30518     
30519     resize : function()
30520     {
30521         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30522         
30523         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30524 //        paragraph.setHeight(paragraph.getWidth());
30525         
30526         if(this.bgimage.length){
30527             var image = this.el.select('.roo-brick-image-view', true).first();
30528             image.setWidth(paragraph.getWidth());
30529             image.setHeight(paragraph.getWidth());
30530         }
30531         
30532     },
30533     
30534     enter: function(e, el)
30535     {
30536         e.preventDefault();
30537         
30538         if(this.bgimage.length){
30539             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30540             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30541         }
30542     },
30543     
30544     leave: function(e, el)
30545     {
30546         e.preventDefault();
30547         
30548         if(this.bgimage.length){
30549             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30550             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30551         }
30552     }
30553     
30554 });
30555
30556  
30557
30558  /*
30559  * Based on:
30560  * Ext JS Library 1.1.1
30561  * Copyright(c) 2006-2007, Ext JS, LLC.
30562  *
30563  * Originally Released Under LGPL - original licence link has changed is not relivant.
30564  *
30565  * Fork - LGPL
30566  * <script type="text/javascript">
30567  */
30568
30569
30570 /**
30571  * @class Roo.bootstrap.SplitBar
30572  * @extends Roo.util.Observable
30573  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30574  * <br><br>
30575  * Usage:
30576  * <pre><code>
30577 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30578                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30579 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30580 split.minSize = 100;
30581 split.maxSize = 600;
30582 split.animate = true;
30583 split.on('moved', splitterMoved);
30584 </code></pre>
30585  * @constructor
30586  * Create a new SplitBar
30587  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30588  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30589  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30590  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30591                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30592                         position of the SplitBar).
30593  */
30594 Roo.bootstrap.SplitBar = function(cfg){
30595     
30596     /** @private */
30597     
30598     //{
30599     //  dragElement : elm
30600     //  resizingElement: el,
30601         // optional..
30602     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30603     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30604         // existingProxy ???
30605     //}
30606     
30607     this.el = Roo.get(cfg.dragElement, true);
30608     this.el.dom.unselectable = "on";
30609     /** @private */
30610     this.resizingEl = Roo.get(cfg.resizingElement, true);
30611
30612     /**
30613      * @private
30614      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30615      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30616      * @type Number
30617      */
30618     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30619     
30620     /**
30621      * The minimum size of the resizing element. (Defaults to 0)
30622      * @type Number
30623      */
30624     this.minSize = 0;
30625     
30626     /**
30627      * The maximum size of the resizing element. (Defaults to 2000)
30628      * @type Number
30629      */
30630     this.maxSize = 2000;
30631     
30632     /**
30633      * Whether to animate the transition to the new size
30634      * @type Boolean
30635      */
30636     this.animate = false;
30637     
30638     /**
30639      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30640      * @type Boolean
30641      */
30642     this.useShim = false;
30643     
30644     /** @private */
30645     this.shim = null;
30646     
30647     if(!cfg.existingProxy){
30648         /** @private */
30649         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30650     }else{
30651         this.proxy = Roo.get(cfg.existingProxy).dom;
30652     }
30653     /** @private */
30654     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30655     
30656     /** @private */
30657     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30658     
30659     /** @private */
30660     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30661     
30662     /** @private */
30663     this.dragSpecs = {};
30664     
30665     /**
30666      * @private The adapter to use to positon and resize elements
30667      */
30668     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30669     this.adapter.init(this);
30670     
30671     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30672         /** @private */
30673         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30674         this.el.addClass("roo-splitbar-h");
30675     }else{
30676         /** @private */
30677         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30678         this.el.addClass("roo-splitbar-v");
30679     }
30680     
30681     this.addEvents({
30682         /**
30683          * @event resize
30684          * Fires when the splitter is moved (alias for {@link #event-moved})
30685          * @param {Roo.bootstrap.SplitBar} this
30686          * @param {Number} newSize the new width or height
30687          */
30688         "resize" : true,
30689         /**
30690          * @event moved
30691          * Fires when the splitter is moved
30692          * @param {Roo.bootstrap.SplitBar} this
30693          * @param {Number} newSize the new width or height
30694          */
30695         "moved" : true,
30696         /**
30697          * @event beforeresize
30698          * Fires before the splitter is dragged
30699          * @param {Roo.bootstrap.SplitBar} this
30700          */
30701         "beforeresize" : true,
30702
30703         "beforeapply" : true
30704     });
30705
30706     Roo.util.Observable.call(this);
30707 };
30708
30709 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30710     onStartProxyDrag : function(x, y){
30711         this.fireEvent("beforeresize", this);
30712         if(!this.overlay){
30713             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30714             o.unselectable();
30715             o.enableDisplayMode("block");
30716             // all splitbars share the same overlay
30717             Roo.bootstrap.SplitBar.prototype.overlay = o;
30718         }
30719         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30720         this.overlay.show();
30721         Roo.get(this.proxy).setDisplayed("block");
30722         var size = this.adapter.getElementSize(this);
30723         this.activeMinSize = this.getMinimumSize();;
30724         this.activeMaxSize = this.getMaximumSize();;
30725         var c1 = size - this.activeMinSize;
30726         var c2 = Math.max(this.activeMaxSize - size, 0);
30727         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30728             this.dd.resetConstraints();
30729             this.dd.setXConstraint(
30730                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30731                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30732             );
30733             this.dd.setYConstraint(0, 0);
30734         }else{
30735             this.dd.resetConstraints();
30736             this.dd.setXConstraint(0, 0);
30737             this.dd.setYConstraint(
30738                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30739                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30740             );
30741          }
30742         this.dragSpecs.startSize = size;
30743         this.dragSpecs.startPoint = [x, y];
30744         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30745     },
30746     
30747     /** 
30748      * @private Called after the drag operation by the DDProxy
30749      */
30750     onEndProxyDrag : function(e){
30751         Roo.get(this.proxy).setDisplayed(false);
30752         var endPoint = Roo.lib.Event.getXY(e);
30753         if(this.overlay){
30754             this.overlay.hide();
30755         }
30756         var newSize;
30757         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30758             newSize = this.dragSpecs.startSize + 
30759                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30760                     endPoint[0] - this.dragSpecs.startPoint[0] :
30761                     this.dragSpecs.startPoint[0] - endPoint[0]
30762                 );
30763         }else{
30764             newSize = this.dragSpecs.startSize + 
30765                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30766                     endPoint[1] - this.dragSpecs.startPoint[1] :
30767                     this.dragSpecs.startPoint[1] - endPoint[1]
30768                 );
30769         }
30770         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30771         if(newSize != this.dragSpecs.startSize){
30772             if(this.fireEvent('beforeapply', this, newSize) !== false){
30773                 this.adapter.setElementSize(this, newSize);
30774                 this.fireEvent("moved", this, newSize);
30775                 this.fireEvent("resize", this, newSize);
30776             }
30777         }
30778     },
30779     
30780     /**
30781      * Get the adapter this SplitBar uses
30782      * @return The adapter object
30783      */
30784     getAdapter : function(){
30785         return this.adapter;
30786     },
30787     
30788     /**
30789      * Set the adapter this SplitBar uses
30790      * @param {Object} adapter A SplitBar adapter object
30791      */
30792     setAdapter : function(adapter){
30793         this.adapter = adapter;
30794         this.adapter.init(this);
30795     },
30796     
30797     /**
30798      * Gets the minimum size for the resizing element
30799      * @return {Number} The minimum size
30800      */
30801     getMinimumSize : function(){
30802         return this.minSize;
30803     },
30804     
30805     /**
30806      * Sets the minimum size for the resizing element
30807      * @param {Number} minSize The minimum size
30808      */
30809     setMinimumSize : function(minSize){
30810         this.minSize = minSize;
30811     },
30812     
30813     /**
30814      * Gets the maximum size for the resizing element
30815      * @return {Number} The maximum size
30816      */
30817     getMaximumSize : function(){
30818         return this.maxSize;
30819     },
30820     
30821     /**
30822      * Sets the maximum size for the resizing element
30823      * @param {Number} maxSize The maximum size
30824      */
30825     setMaximumSize : function(maxSize){
30826         this.maxSize = maxSize;
30827     },
30828     
30829     /**
30830      * Sets the initialize size for the resizing element
30831      * @param {Number} size The initial size
30832      */
30833     setCurrentSize : function(size){
30834         var oldAnimate = this.animate;
30835         this.animate = false;
30836         this.adapter.setElementSize(this, size);
30837         this.animate = oldAnimate;
30838     },
30839     
30840     /**
30841      * Destroy this splitbar. 
30842      * @param {Boolean} removeEl True to remove the element
30843      */
30844     destroy : function(removeEl){
30845         if(this.shim){
30846             this.shim.remove();
30847         }
30848         this.dd.unreg();
30849         this.proxy.parentNode.removeChild(this.proxy);
30850         if(removeEl){
30851             this.el.remove();
30852         }
30853     }
30854 });
30855
30856 /**
30857  * @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.
30858  */
30859 Roo.bootstrap.SplitBar.createProxy = function(dir){
30860     var proxy = new Roo.Element(document.createElement("div"));
30861     proxy.unselectable();
30862     var cls = 'roo-splitbar-proxy';
30863     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30864     document.body.appendChild(proxy.dom);
30865     return proxy.dom;
30866 };
30867
30868 /** 
30869  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30870  * Default Adapter. It assumes the splitter and resizing element are not positioned
30871  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30872  */
30873 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30874 };
30875
30876 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30877     // do nothing for now
30878     init : function(s){
30879     
30880     },
30881     /**
30882      * Called before drag operations to get the current size of the resizing element. 
30883      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30884      */
30885      getElementSize : function(s){
30886         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30887             return s.resizingEl.getWidth();
30888         }else{
30889             return s.resizingEl.getHeight();
30890         }
30891     },
30892     
30893     /**
30894      * Called after drag operations to set the size of the resizing element.
30895      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30896      * @param {Number} newSize The new size to set
30897      * @param {Function} onComplete A function to be invoked when resizing is complete
30898      */
30899     setElementSize : function(s, newSize, onComplete){
30900         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30901             if(!s.animate){
30902                 s.resizingEl.setWidth(newSize);
30903                 if(onComplete){
30904                     onComplete(s, newSize);
30905                 }
30906             }else{
30907                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30908             }
30909         }else{
30910             
30911             if(!s.animate){
30912                 s.resizingEl.setHeight(newSize);
30913                 if(onComplete){
30914                     onComplete(s, newSize);
30915                 }
30916             }else{
30917                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30918             }
30919         }
30920     }
30921 };
30922
30923 /** 
30924  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
30925  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
30926  * Adapter that  moves the splitter element to align with the resized sizing element. 
30927  * Used with an absolute positioned SplitBar.
30928  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30929  * document.body, make sure you assign an id to the body element.
30930  */
30931 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
30932     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30933     this.container = Roo.get(container);
30934 };
30935
30936 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
30937     init : function(s){
30938         this.basic.init(s);
30939     },
30940     
30941     getElementSize : function(s){
30942         return this.basic.getElementSize(s);
30943     },
30944     
30945     setElementSize : function(s, newSize, onComplete){
30946         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
30947     },
30948     
30949     moveSplitter : function(s){
30950         var yes = Roo.bootstrap.SplitBar;
30951         switch(s.placement){
30952             case yes.LEFT:
30953                 s.el.setX(s.resizingEl.getRight());
30954                 break;
30955             case yes.RIGHT:
30956                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
30957                 break;
30958             case yes.TOP:
30959                 s.el.setY(s.resizingEl.getBottom());
30960                 break;
30961             case yes.BOTTOM:
30962                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
30963                 break;
30964         }
30965     }
30966 };
30967
30968 /**
30969  * Orientation constant - Create a vertical SplitBar
30970  * @static
30971  * @type Number
30972  */
30973 Roo.bootstrap.SplitBar.VERTICAL = 1;
30974
30975 /**
30976  * Orientation constant - Create a horizontal SplitBar
30977  * @static
30978  * @type Number
30979  */
30980 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
30981
30982 /**
30983  * Placement constant - The resizing element is to the left of the splitter element
30984  * @static
30985  * @type Number
30986  */
30987 Roo.bootstrap.SplitBar.LEFT = 1;
30988
30989 /**
30990  * Placement constant - The resizing element is to the right of the splitter element
30991  * @static
30992  * @type Number
30993  */
30994 Roo.bootstrap.SplitBar.RIGHT = 2;
30995
30996 /**
30997  * Placement constant - The resizing element is positioned above the splitter element
30998  * @static
30999  * @type Number
31000  */
31001 Roo.bootstrap.SplitBar.TOP = 3;
31002
31003 /**
31004  * Placement constant - The resizing element is positioned under splitter element
31005  * @static
31006  * @type Number
31007  */
31008 Roo.bootstrap.SplitBar.BOTTOM = 4;
31009 Roo.namespace("Roo.bootstrap.layout");/*
31010  * Based on:
31011  * Ext JS Library 1.1.1
31012  * Copyright(c) 2006-2007, Ext JS, LLC.
31013  *
31014  * Originally Released Under LGPL - original licence link has changed is not relivant.
31015  *
31016  * Fork - LGPL
31017  * <script type="text/javascript">
31018  */
31019  
31020 /**
31021  * @class Roo.bootstrap.layout.Manager
31022  * @extends Roo.bootstrap.Component
31023  * Base class for layout managers.
31024  */
31025 Roo.bootstrap.layout.Manager = function(config)
31026 {
31027     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31028     this.el = Roo.get(config.el);
31029     // ie scrollbar fix
31030     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31031         document.body.scroll = "no";
31032     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31033         this.el.position('relative');
31034     }
31035     
31036     this.id = this.el.id;
31037     this.el.addClass("roo-layout-container");
31038     /** false to disable window resize monitoring @type Boolean */
31039     this.monitorWindowResize = true;
31040     this.regions = {};
31041     this.addEvents({
31042         /**
31043          * @event layout
31044          * Fires when a layout is performed. 
31045          * @param {Roo.LayoutManager} this
31046          */
31047         "layout" : true,
31048         /**
31049          * @event regionresized
31050          * Fires when the user resizes a region. 
31051          * @param {Roo.LayoutRegion} region The resized region
31052          * @param {Number} newSize The new size (width for east/west, height for north/south)
31053          */
31054         "regionresized" : true,
31055         /**
31056          * @event regioncollapsed
31057          * Fires when a region is collapsed. 
31058          * @param {Roo.LayoutRegion} region The collapsed region
31059          */
31060         "regioncollapsed" : true,
31061         /**
31062          * @event regionexpanded
31063          * Fires when a region is expanded.  
31064          * @param {Roo.LayoutRegion} region The expanded region
31065          */
31066         "regionexpanded" : true
31067     });
31068     this.updating = false;
31069     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31070 };
31071
31072 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31073     
31074     
31075     regions : null,
31076     
31077     monitorWindowResize : true,
31078     
31079     
31080     updating : false,
31081     
31082     /**
31083      * Returns true if this layout is currently being updated
31084      * @return {Boolean}
31085      */
31086     isUpdating : function(){
31087         return this.updating; 
31088     },
31089     
31090     /**
31091      * Suspend the LayoutManager from doing auto-layouts while
31092      * making multiple add or remove calls
31093      */
31094     beginUpdate : function(){
31095         this.updating = true;    
31096     },
31097     
31098     /**
31099      * Restore auto-layouts and optionally disable the manager from performing a layout
31100      * @param {Boolean} noLayout true to disable a layout update 
31101      */
31102     endUpdate : function(noLayout){
31103         this.updating = false;
31104         if(!noLayout){
31105             this.layout();
31106         }    
31107     },
31108     
31109     layout: function(){
31110         // abstract...
31111     },
31112     
31113     onRegionResized : function(region, newSize){
31114         this.fireEvent("regionresized", region, newSize);
31115         this.layout();
31116     },
31117     
31118     onRegionCollapsed : function(region){
31119         this.fireEvent("regioncollapsed", region);
31120     },
31121     
31122     onRegionExpanded : function(region){
31123         this.fireEvent("regionexpanded", region);
31124     },
31125         
31126     /**
31127      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31128      * performs box-model adjustments.
31129      * @return {Object} The size as an object {width: (the width), height: (the height)}
31130      */
31131     getViewSize : function()
31132     {
31133         var size;
31134         if(this.el.dom != document.body){
31135             size = this.el.getSize();
31136         }else{
31137             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31138         }
31139         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31140         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31141         return size;
31142     },
31143     
31144     /**
31145      * Returns the Element this layout is bound to.
31146      * @return {Roo.Element}
31147      */
31148     getEl : function(){
31149         return this.el;
31150     },
31151     
31152     /**
31153      * Returns the specified region.
31154      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31155      * @return {Roo.LayoutRegion}
31156      */
31157     getRegion : function(target){
31158         return this.regions[target.toLowerCase()];
31159     },
31160     
31161     onWindowResize : function(){
31162         if(this.monitorWindowResize){
31163             this.layout();
31164         }
31165     }
31166 });/*
31167  * Based on:
31168  * Ext JS Library 1.1.1
31169  * Copyright(c) 2006-2007, Ext JS, LLC.
31170  *
31171  * Originally Released Under LGPL - original licence link has changed is not relivant.
31172  *
31173  * Fork - LGPL
31174  * <script type="text/javascript">
31175  */
31176 /**
31177  * @class Roo.bootstrap.layout.Border
31178  * @extends Roo.bootstrap.layout.Manager
31179  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31180  * please see: examples/bootstrap/nested.html<br><br>
31181  
31182 <b>The container the layout is rendered into can be either the body element or any other element.
31183 If it is not the body element, the container needs to either be an absolute positioned element,
31184 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31185 the container size if it is not the body element.</b>
31186
31187 * @constructor
31188 * Create a new Border
31189 * @param {Object} config Configuration options
31190  */
31191 Roo.bootstrap.layout.Border = function(config){
31192     config = config || {};
31193     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31194     
31195     
31196     
31197     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31198         if(config[region]){
31199             config[region].region = region;
31200             this.addRegion(config[region]);
31201         }
31202     },this);
31203     
31204 };
31205
31206 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31207
31208 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31209     /**
31210      * Creates and adds a new region if it doesn't already exist.
31211      * @param {String} target The target region key (north, south, east, west or center).
31212      * @param {Object} config The regions config object
31213      * @return {BorderLayoutRegion} The new region
31214      */
31215     addRegion : function(config)
31216     {
31217         if(!this.regions[config.region]){
31218             var r = this.factory(config);
31219             this.bindRegion(r);
31220         }
31221         return this.regions[config.region];
31222     },
31223
31224     // private (kinda)
31225     bindRegion : function(r){
31226         this.regions[r.config.region] = r;
31227         
31228         r.on("visibilitychange",    this.layout, this);
31229         r.on("paneladded",          this.layout, this);
31230         r.on("panelremoved",        this.layout, this);
31231         r.on("invalidated",         this.layout, this);
31232         r.on("resized",             this.onRegionResized, this);
31233         r.on("collapsed",           this.onRegionCollapsed, this);
31234         r.on("expanded",            this.onRegionExpanded, this);
31235     },
31236
31237     /**
31238      * Performs a layout update.
31239      */
31240     layout : function()
31241     {
31242         if(this.updating) {
31243             return;
31244         }
31245         var size = this.getViewSize();
31246         var w = size.width;
31247         var h = size.height;
31248         var centerW = w;
31249         var centerH = h;
31250         var centerY = 0;
31251         var centerX = 0;
31252         //var x = 0, y = 0;
31253
31254         var rs = this.regions;
31255         var north = rs["north"];
31256         var south = rs["south"]; 
31257         var west = rs["west"];
31258         var east = rs["east"];
31259         var center = rs["center"];
31260         //if(this.hideOnLayout){ // not supported anymore
31261             //c.el.setStyle("display", "none");
31262         //}
31263         if(north && north.isVisible()){
31264             var b = north.getBox();
31265             var m = north.getMargins();
31266             b.width = w - (m.left+m.right);
31267             b.x = m.left;
31268             b.y = m.top;
31269             centerY = b.height + b.y + m.bottom;
31270             centerH -= centerY;
31271             north.updateBox(this.safeBox(b));
31272         }
31273         if(south && south.isVisible()){
31274             var b = south.getBox();
31275             var m = south.getMargins();
31276             b.width = w - (m.left+m.right);
31277             b.x = m.left;
31278             var totalHeight = (b.height + m.top + m.bottom);
31279             b.y = h - totalHeight + m.top;
31280             centerH -= totalHeight;
31281             south.updateBox(this.safeBox(b));
31282         }
31283         if(west && west.isVisible()){
31284             var b = west.getBox();
31285             var m = west.getMargins();
31286             b.height = centerH - (m.top+m.bottom);
31287             b.x = m.left;
31288             b.y = centerY + m.top;
31289             var totalWidth = (b.width + m.left + m.right);
31290             centerX += totalWidth;
31291             centerW -= totalWidth;
31292             west.updateBox(this.safeBox(b));
31293         }
31294         if(east && east.isVisible()){
31295             var b = east.getBox();
31296             var m = east.getMargins();
31297             b.height = centerH - (m.top+m.bottom);
31298             var totalWidth = (b.width + m.left + m.right);
31299             b.x = w - totalWidth + m.left;
31300             b.y = centerY + m.top;
31301             centerW -= totalWidth;
31302             east.updateBox(this.safeBox(b));
31303         }
31304         if(center){
31305             var m = center.getMargins();
31306             var centerBox = {
31307                 x: centerX + m.left,
31308                 y: centerY + m.top,
31309                 width: centerW - (m.left+m.right),
31310                 height: centerH - (m.top+m.bottom)
31311             };
31312             //if(this.hideOnLayout){
31313                 //center.el.setStyle("display", "block");
31314             //}
31315             center.updateBox(this.safeBox(centerBox));
31316         }
31317         this.el.repaint();
31318         this.fireEvent("layout", this);
31319     },
31320
31321     // private
31322     safeBox : function(box){
31323         box.width = Math.max(0, box.width);
31324         box.height = Math.max(0, box.height);
31325         return box;
31326     },
31327
31328     /**
31329      * Adds a ContentPanel (or subclass) to this layout.
31330      * @param {String} target The target region key (north, south, east, west or center).
31331      * @param {Roo.ContentPanel} panel The panel to add
31332      * @return {Roo.ContentPanel} The added panel
31333      */
31334     add : function(target, panel){
31335          
31336         target = target.toLowerCase();
31337         return this.regions[target].add(panel);
31338     },
31339
31340     /**
31341      * Remove a ContentPanel (or subclass) to this layout.
31342      * @param {String} target The target region key (north, south, east, west or center).
31343      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31344      * @return {Roo.ContentPanel} The removed panel
31345      */
31346     remove : function(target, panel){
31347         target = target.toLowerCase();
31348         return this.regions[target].remove(panel);
31349     },
31350
31351     /**
31352      * Searches all regions for a panel with the specified id
31353      * @param {String} panelId
31354      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31355      */
31356     findPanel : function(panelId){
31357         var rs = this.regions;
31358         for(var target in rs){
31359             if(typeof rs[target] != "function"){
31360                 var p = rs[target].getPanel(panelId);
31361                 if(p){
31362                     return p;
31363                 }
31364             }
31365         }
31366         return null;
31367     },
31368
31369     /**
31370      * Searches all regions for a panel with the specified id and activates (shows) it.
31371      * @param {String/ContentPanel} panelId The panels id or the panel itself
31372      * @return {Roo.ContentPanel} The shown panel or null
31373      */
31374     showPanel : function(panelId) {
31375       var rs = this.regions;
31376       for(var target in rs){
31377          var r = rs[target];
31378          if(typeof r != "function"){
31379             if(r.hasPanel(panelId)){
31380                return r.showPanel(panelId);
31381             }
31382          }
31383       }
31384       return null;
31385    },
31386
31387    /**
31388      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31389      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31390      */
31391    /*
31392     restoreState : function(provider){
31393         if(!provider){
31394             provider = Roo.state.Manager;
31395         }
31396         var sm = new Roo.LayoutStateManager();
31397         sm.init(this, provider);
31398     },
31399 */
31400  
31401  
31402     /**
31403      * Adds a xtype elements to the layout.
31404      * <pre><code>
31405
31406 layout.addxtype({
31407        xtype : 'ContentPanel',
31408        region: 'west',
31409        items: [ .... ]
31410    }
31411 );
31412
31413 layout.addxtype({
31414         xtype : 'NestedLayoutPanel',
31415         region: 'west',
31416         layout: {
31417            center: { },
31418            west: { }   
31419         },
31420         items : [ ... list of content panels or nested layout panels.. ]
31421    }
31422 );
31423 </code></pre>
31424      * @param {Object} cfg Xtype definition of item to add.
31425      */
31426     addxtype : function(cfg)
31427     {
31428         // basically accepts a pannel...
31429         // can accept a layout region..!?!?
31430         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31431         
31432         
31433         // theory?  children can only be panels??
31434         
31435         //if (!cfg.xtype.match(/Panel$/)) {
31436         //    return false;
31437         //}
31438         var ret = false;
31439         
31440         if (typeof(cfg.region) == 'undefined') {
31441             Roo.log("Failed to add Panel, region was not set");
31442             Roo.log(cfg);
31443             return false;
31444         }
31445         var region = cfg.region;
31446         delete cfg.region;
31447         
31448           
31449         var xitems = [];
31450         if (cfg.items) {
31451             xitems = cfg.items;
31452             delete cfg.items;
31453         }
31454         var nb = false;
31455         
31456         switch(cfg.xtype) 
31457         {
31458             case 'Content':  // ContentPanel (el, cfg)
31459             case 'Scroll':  // ContentPanel (el, cfg)
31460             case 'View': 
31461                 cfg.autoCreate = true;
31462                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31463                 //} else {
31464                 //    var el = this.el.createChild();
31465                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31466                 //}
31467                 
31468                 this.add(region, ret);
31469                 break;
31470             
31471             /*
31472             case 'TreePanel': // our new panel!
31473                 cfg.el = this.el.createChild();
31474                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31475                 this.add(region, ret);
31476                 break;
31477             */
31478             
31479             case 'Nest': 
31480                 // create a new Layout (which is  a Border Layout...
31481                 
31482                 var clayout = cfg.layout;
31483                 clayout.el  = this.el.createChild();
31484                 clayout.items   = clayout.items  || [];
31485                 
31486                 delete cfg.layout;
31487                 
31488                 // replace this exitems with the clayout ones..
31489                 xitems = clayout.items;
31490                  
31491                 // force background off if it's in center...
31492                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31493                     cfg.background = false;
31494                 }
31495                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31496                 
31497                 
31498                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31499                 //console.log('adding nested layout panel '  + cfg.toSource());
31500                 this.add(region, ret);
31501                 nb = {}; /// find first...
31502                 break;
31503             
31504             case 'Grid':
31505                 
31506                 // needs grid and region
31507                 
31508                 //var el = this.getRegion(region).el.createChild();
31509                 var el = this.el.createChild();
31510                 // create the grid first...
31511                 cfg.grid.container = el;
31512                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31513                 
31514                 
31515                 if (region == 'center' && this.active ) {
31516                     cfg.background = false;
31517                 }
31518                 
31519                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31520                 
31521                 this.add(region, ret);
31522                 
31523                 if (cfg.background) {
31524                     // render grid on panel activation (if panel background)
31525                     ret.on('activate', function(gp) {
31526                         if (!gp.grid.rendered) {
31527                             gp.grid.render(gp.grid.getGridEl());
31528                         }
31529                     });
31530                 } else {
31531                     cfg.grid.render(cfg.grid.getGridEl());
31532                 }
31533                 break;
31534            
31535            
31536             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31537                 // it was the old xcomponent building that caused this before.
31538                 // espeically if border is the top element in the tree.
31539                 ret = this;
31540                 break; 
31541                 
31542                     
31543                 
31544                 
31545                 
31546             default:
31547                 /*
31548                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31549                     
31550                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31551                     this.add(region, ret);
31552                 } else {
31553                 */
31554                     Roo.log(cfg);
31555                     throw "Can not add '" + cfg.xtype + "' to Border";
31556                     return null;
31557              
31558                                 
31559              
31560         }
31561         this.beginUpdate();
31562         // add children..
31563         var region = '';
31564         var abn = {};
31565         Roo.each(xitems, function(i)  {
31566             region = nb && i.region ? i.region : false;
31567             
31568             var add = ret.addxtype(i);
31569            
31570             if (region) {
31571                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31572                 if (!i.background) {
31573                     abn[region] = nb[region] ;
31574                 }
31575             }
31576             
31577         });
31578         this.endUpdate();
31579
31580         // make the last non-background panel active..
31581         //if (nb) { Roo.log(abn); }
31582         if (nb) {
31583             
31584             for(var r in abn) {
31585                 region = this.getRegion(r);
31586                 if (region) {
31587                     // tried using nb[r], but it does not work..
31588                      
31589                     region.showPanel(abn[r]);
31590                    
31591                 }
31592             }
31593         }
31594         return ret;
31595         
31596     },
31597     
31598     
31599 // private
31600     factory : function(cfg)
31601     {
31602         
31603         var validRegions = Roo.bootstrap.layout.Border.regions;
31604
31605         var target = cfg.region;
31606         cfg.mgr = this;
31607         
31608         var r = Roo.bootstrap.layout;
31609         Roo.log(target);
31610         switch(target){
31611             case "north":
31612                 return new r.North(cfg);
31613             case "south":
31614                 return new r.South(cfg);
31615             case "east":
31616                 return new r.East(cfg);
31617             case "west":
31618                 return new r.West(cfg);
31619             case "center":
31620                 return new r.Center(cfg);
31621         }
31622         throw 'Layout region "'+target+'" not supported.';
31623     }
31624     
31625     
31626 });
31627  /*
31628  * Based on:
31629  * Ext JS Library 1.1.1
31630  * Copyright(c) 2006-2007, Ext JS, LLC.
31631  *
31632  * Originally Released Under LGPL - original licence link has changed is not relivant.
31633  *
31634  * Fork - LGPL
31635  * <script type="text/javascript">
31636  */
31637  
31638 /**
31639  * @class Roo.bootstrap.layout.Basic
31640  * @extends Roo.util.Observable
31641  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31642  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31643  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31644  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31645  * @cfg {string}   region  the region that it inhabits..
31646  * @cfg {bool}   skipConfig skip config?
31647  * 
31648
31649  */
31650 Roo.bootstrap.layout.Basic = function(config){
31651     
31652     this.mgr = config.mgr;
31653     
31654     this.position = config.region;
31655     
31656     var skipConfig = config.skipConfig;
31657     
31658     this.events = {
31659         /**
31660          * @scope Roo.BasicLayoutRegion
31661          */
31662         
31663         /**
31664          * @event beforeremove
31665          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31666          * @param {Roo.LayoutRegion} this
31667          * @param {Roo.ContentPanel} panel The panel
31668          * @param {Object} e The cancel event object
31669          */
31670         "beforeremove" : true,
31671         /**
31672          * @event invalidated
31673          * Fires when the layout for this region is changed.
31674          * @param {Roo.LayoutRegion} this
31675          */
31676         "invalidated" : true,
31677         /**
31678          * @event visibilitychange
31679          * Fires when this region is shown or hidden 
31680          * @param {Roo.LayoutRegion} this
31681          * @param {Boolean} visibility true or false
31682          */
31683         "visibilitychange" : true,
31684         /**
31685          * @event paneladded
31686          * Fires when a panel is added. 
31687          * @param {Roo.LayoutRegion} this
31688          * @param {Roo.ContentPanel} panel The panel
31689          */
31690         "paneladded" : true,
31691         /**
31692          * @event panelremoved
31693          * Fires when a panel is removed. 
31694          * @param {Roo.LayoutRegion} this
31695          * @param {Roo.ContentPanel} panel The panel
31696          */
31697         "panelremoved" : true,
31698         /**
31699          * @event beforecollapse
31700          * Fires when this region before collapse.
31701          * @param {Roo.LayoutRegion} this
31702          */
31703         "beforecollapse" : true,
31704         /**
31705          * @event collapsed
31706          * Fires when this region is collapsed.
31707          * @param {Roo.LayoutRegion} this
31708          */
31709         "collapsed" : true,
31710         /**
31711          * @event expanded
31712          * Fires when this region is expanded.
31713          * @param {Roo.LayoutRegion} this
31714          */
31715         "expanded" : true,
31716         /**
31717          * @event slideshow
31718          * Fires when this region is slid into view.
31719          * @param {Roo.LayoutRegion} this
31720          */
31721         "slideshow" : true,
31722         /**
31723          * @event slidehide
31724          * Fires when this region slides out of view. 
31725          * @param {Roo.LayoutRegion} this
31726          */
31727         "slidehide" : true,
31728         /**
31729          * @event panelactivated
31730          * Fires when a panel is activated. 
31731          * @param {Roo.LayoutRegion} this
31732          * @param {Roo.ContentPanel} panel The activated panel
31733          */
31734         "panelactivated" : true,
31735         /**
31736          * @event resized
31737          * Fires when the user resizes this region. 
31738          * @param {Roo.LayoutRegion} this
31739          * @param {Number} newSize The new size (width for east/west, height for north/south)
31740          */
31741         "resized" : true
31742     };
31743     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31744     this.panels = new Roo.util.MixedCollection();
31745     this.panels.getKey = this.getPanelId.createDelegate(this);
31746     this.box = null;
31747     this.activePanel = null;
31748     // ensure listeners are added...
31749     
31750     if (config.listeners || config.events) {
31751         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31752             listeners : config.listeners || {},
31753             events : config.events || {}
31754         });
31755     }
31756     
31757     if(skipConfig !== true){
31758         this.applyConfig(config);
31759     }
31760 };
31761
31762 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31763 {
31764     getPanelId : function(p){
31765         return p.getId();
31766     },
31767     
31768     applyConfig : function(config){
31769         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31770         this.config = config;
31771         
31772     },
31773     
31774     /**
31775      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31776      * the width, for horizontal (north, south) the height.
31777      * @param {Number} newSize The new width or height
31778      */
31779     resizeTo : function(newSize){
31780         var el = this.el ? this.el :
31781                  (this.activePanel ? this.activePanel.getEl() : null);
31782         if(el){
31783             switch(this.position){
31784                 case "east":
31785                 case "west":
31786                     el.setWidth(newSize);
31787                     this.fireEvent("resized", this, newSize);
31788                 break;
31789                 case "north":
31790                 case "south":
31791                     el.setHeight(newSize);
31792                     this.fireEvent("resized", this, newSize);
31793                 break;                
31794             }
31795         }
31796     },
31797     
31798     getBox : function(){
31799         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31800     },
31801     
31802     getMargins : function(){
31803         return this.margins;
31804     },
31805     
31806     updateBox : function(box){
31807         this.box = box;
31808         var el = this.activePanel.getEl();
31809         el.dom.style.left = box.x + "px";
31810         el.dom.style.top = box.y + "px";
31811         this.activePanel.setSize(box.width, box.height);
31812     },
31813     
31814     /**
31815      * Returns the container element for this region.
31816      * @return {Roo.Element}
31817      */
31818     getEl : function(){
31819         return this.activePanel;
31820     },
31821     
31822     /**
31823      * Returns true if this region is currently visible.
31824      * @return {Boolean}
31825      */
31826     isVisible : function(){
31827         return this.activePanel ? true : false;
31828     },
31829     
31830     setActivePanel : function(panel){
31831         panel = this.getPanel(panel);
31832         if(this.activePanel && this.activePanel != panel){
31833             this.activePanel.setActiveState(false);
31834             this.activePanel.getEl().setLeftTop(-10000,-10000);
31835         }
31836         this.activePanel = panel;
31837         panel.setActiveState(true);
31838         if(this.box){
31839             panel.setSize(this.box.width, this.box.height);
31840         }
31841         this.fireEvent("panelactivated", this, panel);
31842         this.fireEvent("invalidated");
31843     },
31844     
31845     /**
31846      * Show the specified panel.
31847      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31848      * @return {Roo.ContentPanel} The shown panel or null
31849      */
31850     showPanel : function(panel){
31851         panel = this.getPanel(panel);
31852         if(panel){
31853             this.setActivePanel(panel);
31854         }
31855         return panel;
31856     },
31857     
31858     /**
31859      * Get the active panel for this region.
31860      * @return {Roo.ContentPanel} The active panel or null
31861      */
31862     getActivePanel : function(){
31863         return this.activePanel;
31864     },
31865     
31866     /**
31867      * Add the passed ContentPanel(s)
31868      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31869      * @return {Roo.ContentPanel} The panel added (if only one was added)
31870      */
31871     add : function(panel){
31872         if(arguments.length > 1){
31873             for(var i = 0, len = arguments.length; i < len; i++) {
31874                 this.add(arguments[i]);
31875             }
31876             return null;
31877         }
31878         if(this.hasPanel(panel)){
31879             this.showPanel(panel);
31880             return panel;
31881         }
31882         var el = panel.getEl();
31883         if(el.dom.parentNode != this.mgr.el.dom){
31884             this.mgr.el.dom.appendChild(el.dom);
31885         }
31886         if(panel.setRegion){
31887             panel.setRegion(this);
31888         }
31889         this.panels.add(panel);
31890         el.setStyle("position", "absolute");
31891         if(!panel.background){
31892             this.setActivePanel(panel);
31893             if(this.config.initialSize && this.panels.getCount()==1){
31894                 this.resizeTo(this.config.initialSize);
31895             }
31896         }
31897         this.fireEvent("paneladded", this, panel);
31898         return panel;
31899     },
31900     
31901     /**
31902      * Returns true if the panel is in this region.
31903      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31904      * @return {Boolean}
31905      */
31906     hasPanel : function(panel){
31907         if(typeof panel == "object"){ // must be panel obj
31908             panel = panel.getId();
31909         }
31910         return this.getPanel(panel) ? true : false;
31911     },
31912     
31913     /**
31914      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31915      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31916      * @param {Boolean} preservePanel Overrides the config preservePanel option
31917      * @return {Roo.ContentPanel} The panel that was removed
31918      */
31919     remove : function(panel, preservePanel){
31920         panel = this.getPanel(panel);
31921         if(!panel){
31922             return null;
31923         }
31924         var e = {};
31925         this.fireEvent("beforeremove", this, panel, e);
31926         if(e.cancel === true){
31927             return null;
31928         }
31929         var panelId = panel.getId();
31930         this.panels.removeKey(panelId);
31931         return panel;
31932     },
31933     
31934     /**
31935      * Returns the panel specified or null if it's not in this region.
31936      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31937      * @return {Roo.ContentPanel}
31938      */
31939     getPanel : function(id){
31940         if(typeof id == "object"){ // must be panel obj
31941             return id;
31942         }
31943         return this.panels.get(id);
31944     },
31945     
31946     /**
31947      * Returns this regions position (north/south/east/west/center).
31948      * @return {String} 
31949      */
31950     getPosition: function(){
31951         return this.position;    
31952     }
31953 });/*
31954  * Based on:
31955  * Ext JS Library 1.1.1
31956  * Copyright(c) 2006-2007, Ext JS, LLC.
31957  *
31958  * Originally Released Under LGPL - original licence link has changed is not relivant.
31959  *
31960  * Fork - LGPL
31961  * <script type="text/javascript">
31962  */
31963  
31964 /**
31965  * @class Roo.bootstrap.layout.Region
31966  * @extends Roo.bootstrap.layout.Basic
31967  * This class represents a region in a layout manager.
31968  
31969  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31970  * @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})
31971  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31972  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31973  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31974  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31975  * @cfg {String}    title           The title for the region (overrides panel titles)
31976  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31977  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31978  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31979  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31980  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31981  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31982  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31983  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31984  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31985  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
31986
31987  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31988  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31989  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31990  * @cfg {Number}    width           For East/West panels
31991  * @cfg {Number}    height          For North/South panels
31992  * @cfg {Boolean}   split           To show the splitter
31993  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31994  * 
31995  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31996  * @cfg {string}   region  the region that it inhabits..
31997  *
31998
31999  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32000  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32001
32002  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32003  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32004  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32005  */
32006 Roo.bootstrap.layout.Region = function(config)
32007 {
32008     
32009     var mgr = config.mgr;
32010     var pos = config.region;
32011     config.skipConfig = true;
32012     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32013     var dh = Roo.DomHelper;
32014     /** This region's container element 
32015     * @type Roo.Element */
32016     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "roo-layout-region roo-layout-panel roo-layout-panel-" + this.position}, true);
32017     /** This region's title element 
32018     * @type Roo.Element */
32019
32020     this.titleEl = dh.append(this.el.dom,
32021         {
32022                 tag: "div",
32023                 unselectable: "on",
32024                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32025                 children:[
32026                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32027                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32028                 ]}, true);
32029     
32030     this.titleEl.enableDisplayMode();
32031     /** This region's title text element 
32032     * @type HTMLElement */
32033     this.titleTextEl = this.titleEl.dom.firstChild;
32034     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32035     /*
32036     this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32037     this.closeBtn.enableDisplayMode();
32038     this.closeBtn.on("click", this.closeClicked, this);
32039     this.closeBtn.hide();
32040 */
32041     this.createBody(config);
32042     this.visible = true;
32043     this.collapsed = false;
32044
32045     if(config.hideWhenEmpty){
32046         this.hide();
32047         this.on("paneladded", this.validateVisibility, this);
32048         this.on("panelremoved", this.validateVisibility, this);
32049     }
32050     this.applyConfig(config);
32051 };
32052
32053 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32054
32055
32056
32057     createBody : function(){
32058         /** This region's body element 
32059         * @type Roo.Element */
32060         this.bodyEl = this.el.createChild({
32061                 tag: "div",
32062                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32063         });
32064     },
32065
32066     applyConfig : function(c)
32067     {
32068         /*
32069          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32070             var dh = Roo.DomHelper;
32071             if(c.titlebar !== false){
32072                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32073                 this.collapseBtn.on("click", this.collapse, this);
32074                 this.collapseBtn.enableDisplayMode();
32075                 /*
32076                 if(c.showPin === true || this.showPin){
32077                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32078                     this.stickBtn.enableDisplayMode();
32079                     this.stickBtn.on("click", this.expand, this);
32080                     this.stickBtn.hide();
32081                 }
32082                 
32083             }
32084             */
32085             /** This region's collapsed element
32086             * @type Roo.Element */
32087             /*
32088              *
32089             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32090                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32091             ]}, true);
32092             
32093             if(c.floatable !== false){
32094                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32095                this.collapsedEl.on("click", this.collapseClick, this);
32096             }
32097
32098             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32099                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32100                    id: "message", unselectable: "on", style:{"float":"left"}});
32101                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32102              }
32103             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32104             this.expandBtn.on("click", this.expand, this);
32105             
32106         }
32107         
32108         if(this.collapseBtn){
32109             this.collapseBtn.setVisible(c.collapsible == true);
32110         }
32111         
32112         this.cmargins = c.cmargins || this.cmargins ||
32113                          (this.position == "west" || this.position == "east" ?
32114                              {top: 0, left: 2, right:2, bottom: 0} :
32115                              {top: 2, left: 0, right:0, bottom: 2});
32116         */
32117         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32118         
32119         
32120         this.bottomTabs = c.tabPosition != "top";
32121         
32122         this.autoScroll = c.autoScroll || false;
32123         
32124         
32125         if(this.autoScroll){
32126             this.bodyEl.setStyle("overflow", "auto");
32127         }else{
32128             this.bodyEl.setStyle("overflow", c.overflow || 'hidden');
32129         }
32130         //if(c.titlebar !== false){
32131             if((!c.titlebar && !c.title) || c.titlebar === false){
32132                 this.titleEl.hide();
32133             }else{
32134                 this.titleEl.show();
32135                 if(c.title){
32136                     this.titleTextEl.innerHTML = c.title;
32137                 }
32138             }
32139         //}
32140         this.duration = c.duration || .30;
32141         this.slideDuration = c.slideDuration || .45;
32142         this.config = c;
32143         if(c.collapsed){
32144             this.collapse(true);
32145         }
32146         if(c.hidden){
32147             this.hide();
32148         }
32149     },
32150     /**
32151      * Returns true if this region is currently visible.
32152      * @return {Boolean}
32153      */
32154     isVisible : function(){
32155         return this.visible;
32156     },
32157
32158     /**
32159      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32160      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32161      */
32162     //setCollapsedTitle : function(title){
32163     //    title = title || "&#160;";
32164      //   if(this.collapsedTitleTextEl){
32165       //      this.collapsedTitleTextEl.innerHTML = title;
32166        // }
32167     //},
32168
32169     getBox : function(){
32170         var b;
32171       //  if(!this.collapsed){
32172             b = this.el.getBox(false, true);
32173        // }else{
32174           //  b = this.collapsedEl.getBox(false, true);
32175         //}
32176         return b;
32177     },
32178
32179     getMargins : function(){
32180         return this.margins;
32181         //return this.collapsed ? this.cmargins : this.margins;
32182     },
32183 /*
32184     highlight : function(){
32185         this.el.addClass("x-layout-panel-dragover");
32186     },
32187
32188     unhighlight : function(){
32189         this.el.removeClass("x-layout-panel-dragover");
32190     },
32191 */
32192     updateBox : function(box)
32193     {
32194         this.box = box;
32195         if(!this.collapsed){
32196             this.el.dom.style.left = box.x + "px";
32197             this.el.dom.style.top = box.y + "px";
32198             this.updateBody(box.width, box.height);
32199         }else{
32200             this.collapsedEl.dom.style.left = box.x + "px";
32201             this.collapsedEl.dom.style.top = box.y + "px";
32202             this.collapsedEl.setSize(box.width, box.height);
32203         }
32204         if(this.tabs){
32205             this.tabs.autoSizeTabs();
32206         }
32207     },
32208
32209     updateBody : function(w, h)
32210     {
32211         if(w !== null){
32212             this.el.setWidth(w);
32213             w -= this.el.getBorderWidth("rl");
32214             if(this.config.adjustments){
32215                 w += this.config.adjustments[0];
32216             }
32217         }
32218         if(h !== null){
32219             this.el.setHeight(h);
32220             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32221             h -= this.el.getBorderWidth("tb");
32222             if(this.config.adjustments){
32223                 h += this.config.adjustments[1];
32224             }
32225             this.bodyEl.setHeight(h);
32226             if(this.tabs){
32227                 h = this.tabs.syncHeight(h);
32228             }
32229         }
32230         if(this.panelSize){
32231             w = w !== null ? w : this.panelSize.width;
32232             h = h !== null ? h : this.panelSize.height;
32233         }
32234         if(this.activePanel){
32235             var el = this.activePanel.getEl();
32236             w = w !== null ? w : el.getWidth();
32237             h = h !== null ? h : el.getHeight();
32238             this.panelSize = {width: w, height: h};
32239             this.activePanel.setSize(w, h);
32240         }
32241         if(Roo.isIE && this.tabs){
32242             this.tabs.el.repaint();
32243         }
32244     },
32245
32246     /**
32247      * Returns the container element for this region.
32248      * @return {Roo.Element}
32249      */
32250     getEl : function(){
32251         return this.el;
32252     },
32253
32254     /**
32255      * Hides this region.
32256      */
32257     hide : function(){
32258         //if(!this.collapsed){
32259             this.el.dom.style.left = "-2000px";
32260             this.el.hide();
32261         //}else{
32262          //   this.collapsedEl.dom.style.left = "-2000px";
32263          //   this.collapsedEl.hide();
32264        // }
32265         this.visible = false;
32266         this.fireEvent("visibilitychange", this, false);
32267     },
32268
32269     /**
32270      * Shows this region if it was previously hidden.
32271      */
32272     show : function(){
32273         //if(!this.collapsed){
32274             this.el.show();
32275         //}else{
32276         //    this.collapsedEl.show();
32277        // }
32278         this.visible = true;
32279         this.fireEvent("visibilitychange", this, true);
32280     },
32281 /*
32282     closeClicked : function(){
32283         if(this.activePanel){
32284             this.remove(this.activePanel);
32285         }
32286     },
32287
32288     collapseClick : function(e){
32289         if(this.isSlid){
32290            e.stopPropagation();
32291            this.slideIn();
32292         }else{
32293            e.stopPropagation();
32294            this.slideOut();
32295         }
32296     },
32297 */
32298     /**
32299      * Collapses this region.
32300      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32301      */
32302     /*
32303     collapse : function(skipAnim, skipCheck = false){
32304         if(this.collapsed) {
32305             return;
32306         }
32307         
32308         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32309             
32310             this.collapsed = true;
32311             if(this.split){
32312                 this.split.el.hide();
32313             }
32314             if(this.config.animate && skipAnim !== true){
32315                 this.fireEvent("invalidated", this);
32316                 this.animateCollapse();
32317             }else{
32318                 this.el.setLocation(-20000,-20000);
32319                 this.el.hide();
32320                 this.collapsedEl.show();
32321                 this.fireEvent("collapsed", this);
32322                 this.fireEvent("invalidated", this);
32323             }
32324         }
32325         
32326     },
32327 */
32328     animateCollapse : function(){
32329         // overridden
32330     },
32331
32332     /**
32333      * Expands this region if it was previously collapsed.
32334      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32335      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32336      */
32337     /*
32338     expand : function(e, skipAnim){
32339         if(e) {
32340             e.stopPropagation();
32341         }
32342         if(!this.collapsed || this.el.hasActiveFx()) {
32343             return;
32344         }
32345         if(this.isSlid){
32346             this.afterSlideIn();
32347             skipAnim = true;
32348         }
32349         this.collapsed = false;
32350         if(this.config.animate && skipAnim !== true){
32351             this.animateExpand();
32352         }else{
32353             this.el.show();
32354             if(this.split){
32355                 this.split.el.show();
32356             }
32357             this.collapsedEl.setLocation(-2000,-2000);
32358             this.collapsedEl.hide();
32359             this.fireEvent("invalidated", this);
32360             this.fireEvent("expanded", this);
32361         }
32362     },
32363 */
32364     animateExpand : function(){
32365         // overridden
32366     },
32367
32368     initTabs : function()
32369     {
32370         this.bodyEl.setStyle("overflow", "hidden");
32371         var ts = new Roo.bootstrap.panel.Tabs({
32372                 el: this.bodyEl.dom,
32373                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32374                 disableTooltips: this.config.disableTabTips,
32375                 toolbar : this.config.toolbar
32376             });
32377         
32378         if(this.config.hideTabs){
32379             ts.stripWrap.setDisplayed(false);
32380         }
32381         this.tabs = ts;
32382         ts.resizeTabs = this.config.resizeTabs === true;
32383         ts.minTabWidth = this.config.minTabWidth || 40;
32384         ts.maxTabWidth = this.config.maxTabWidth || 250;
32385         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32386         ts.monitorResize = false;
32387         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32388         ts.bodyEl.addClass('roo-layout-tabs-body');
32389         this.panels.each(this.initPanelAsTab, this);
32390     },
32391
32392     initPanelAsTab : function(panel){
32393         var ti = this.tabs.addTab(
32394                     panel.getEl().id,
32395                     panel.getTitle(), null,
32396                     this.config.closeOnTab && panel.isClosable()
32397             );
32398         if(panel.tabTip !== undefined){
32399             ti.setTooltip(panel.tabTip);
32400         }
32401         ti.on("activate", function(){
32402               this.setActivePanel(panel);
32403         }, this);
32404         
32405         if(this.config.closeOnTab){
32406             ti.on("beforeclose", function(t, e){
32407                 e.cancel = true;
32408                 this.remove(panel);
32409             }, this);
32410         }
32411         return ti;
32412     },
32413
32414     updatePanelTitle : function(panel, title)
32415     {
32416         if(this.activePanel == panel){
32417             this.updateTitle(title);
32418         }
32419         if(this.tabs){
32420             var ti = this.tabs.getTab(panel.getEl().id);
32421             ti.setText(title);
32422             if(panel.tabTip !== undefined){
32423                 ti.setTooltip(panel.tabTip);
32424             }
32425         }
32426     },
32427
32428     updateTitle : function(title){
32429         if(this.titleTextEl && !this.config.title){
32430             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32431         }
32432     },
32433
32434     setActivePanel : function(panel)
32435     {
32436         panel = this.getPanel(panel);
32437         if(this.activePanel && this.activePanel != panel){
32438             this.activePanel.setActiveState(false);
32439         }
32440         this.activePanel = panel;
32441         panel.setActiveState(true);
32442         if(this.panelSize){
32443             panel.setSize(this.panelSize.width, this.panelSize.height);
32444         }
32445         if(this.closeBtn){
32446             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32447         }
32448         this.updateTitle(panel.getTitle());
32449         if(this.tabs){
32450             this.fireEvent("invalidated", this);
32451         }
32452         this.fireEvent("panelactivated", this, panel);
32453     },
32454
32455     /**
32456      * Shows the specified panel.
32457      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32458      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32459      */
32460     showPanel : function(panel)
32461     {
32462         panel = this.getPanel(panel);
32463         if(panel){
32464             if(this.tabs){
32465                 var tab = this.tabs.getTab(panel.getEl().id);
32466                 if(tab.isHidden()){
32467                     this.tabs.unhideTab(tab.id);
32468                 }
32469                 tab.activate();
32470             }else{
32471                 this.setActivePanel(panel);
32472             }
32473         }
32474         return panel;
32475     },
32476
32477     /**
32478      * Get the active panel for this region.
32479      * @return {Roo.ContentPanel} The active panel or null
32480      */
32481     getActivePanel : function(){
32482         return this.activePanel;
32483     },
32484
32485     validateVisibility : function(){
32486         if(this.panels.getCount() < 1){
32487             this.updateTitle("&#160;");
32488             this.closeBtn.hide();
32489             this.hide();
32490         }else{
32491             if(!this.isVisible()){
32492                 this.show();
32493             }
32494         }
32495     },
32496
32497     /**
32498      * Adds the passed ContentPanel(s) to this region.
32499      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32500      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32501      */
32502     add : function(panel){
32503         if(arguments.length > 1){
32504             for(var i = 0, len = arguments.length; i < len; i++) {
32505                 this.add(arguments[i]);
32506             }
32507             return null;
32508         }
32509         if(this.hasPanel(panel)){
32510             this.showPanel(panel);
32511             return panel;
32512         }
32513         panel.setRegion(this);
32514         this.panels.add(panel);
32515         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32516             this.bodyEl.dom.appendChild(panel.getEl().dom);
32517             if(panel.background !== true){
32518                 this.setActivePanel(panel);
32519             }
32520             this.fireEvent("paneladded", this, panel);
32521             return panel;
32522         }
32523         if(!this.tabs){
32524             this.initTabs();
32525         }else{
32526             this.initPanelAsTab(panel);
32527         }
32528         
32529         
32530         if(panel.background !== true){
32531             this.tabs.activate(panel.getEl().id);
32532         }
32533         this.fireEvent("paneladded", this, panel);
32534         return panel;
32535     },
32536
32537     /**
32538      * Hides the tab for the specified panel.
32539      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32540      */
32541     hidePanel : function(panel){
32542         if(this.tabs && (panel = this.getPanel(panel))){
32543             this.tabs.hideTab(panel.getEl().id);
32544         }
32545     },
32546
32547     /**
32548      * Unhides the tab for a previously hidden panel.
32549      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32550      */
32551     unhidePanel : function(panel){
32552         if(this.tabs && (panel = this.getPanel(panel))){
32553             this.tabs.unhideTab(panel.getEl().id);
32554         }
32555     },
32556
32557     clearPanels : function(){
32558         while(this.panels.getCount() > 0){
32559              this.remove(this.panels.first());
32560         }
32561     },
32562
32563     /**
32564      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32565      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32566      * @param {Boolean} preservePanel Overrides the config preservePanel option
32567      * @return {Roo.ContentPanel} The panel that was removed
32568      */
32569     remove : function(panel, preservePanel)
32570     {
32571         panel = this.getPanel(panel);
32572         if(!panel){
32573             return null;
32574         }
32575         var e = {};
32576         this.fireEvent("beforeremove", this, panel, e);
32577         if(e.cancel === true){
32578             return null;
32579         }
32580         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32581         var panelId = panel.getId();
32582         this.panels.removeKey(panelId);
32583         if(preservePanel){
32584             document.body.appendChild(panel.getEl().dom);
32585         }
32586         if(this.tabs){
32587             this.tabs.removeTab(panel.getEl().id);
32588         }else if (!preservePanel){
32589             this.bodyEl.dom.removeChild(panel.getEl().dom);
32590         }
32591         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32592             var p = this.panels.first();
32593             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32594             tempEl.appendChild(p.getEl().dom);
32595             this.bodyEl.update("");
32596             this.bodyEl.dom.appendChild(p.getEl().dom);
32597             tempEl = null;
32598             this.updateTitle(p.getTitle());
32599             this.tabs = null;
32600             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32601             this.setActivePanel(p);
32602         }
32603         panel.setRegion(null);
32604         if(this.activePanel == panel){
32605             this.activePanel = null;
32606         }
32607         if(this.config.autoDestroy !== false && preservePanel !== true){
32608             try{panel.destroy();}catch(e){}
32609         }
32610         this.fireEvent("panelremoved", this, panel);
32611         return panel;
32612     },
32613
32614     /**
32615      * Returns the TabPanel component used by this region
32616      * @return {Roo.TabPanel}
32617      */
32618     getTabs : function(){
32619         return this.tabs;
32620     },
32621
32622     createTool : function(parentEl, className){
32623         var btn = Roo.DomHelper.append(parentEl, {
32624             tag: "div",
32625             cls: "x-layout-tools-button",
32626             children: [ {
32627                 tag: "div",
32628                 cls: "roo-layout-tools-button-inner " + className,
32629                 html: "&#160;"
32630             }]
32631         }, true);
32632         btn.addClassOnOver("roo-layout-tools-button-over");
32633         return btn;
32634     }
32635 });/*
32636  * Based on:
32637  * Ext JS Library 1.1.1
32638  * Copyright(c) 2006-2007, Ext JS, LLC.
32639  *
32640  * Originally Released Under LGPL - original licence link has changed is not relivant.
32641  *
32642  * Fork - LGPL
32643  * <script type="text/javascript">
32644  */
32645  
32646
32647
32648 /**
32649  * @class Roo.SplitLayoutRegion
32650  * @extends Roo.LayoutRegion
32651  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32652  */
32653 Roo.bootstrap.layout.Split = function(config){
32654     this.cursor = config.cursor;
32655     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32656 };
32657
32658 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32659 {
32660     splitTip : "Drag to resize.",
32661     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32662     useSplitTips : false,
32663
32664     applyConfig : function(config){
32665         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32666         
32667         if(config.split){
32668             if(!this.split){
32669                 
32670                 
32671                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,  {
32672                                 tag: "div",
32673                                 id: this.el.id + "-split",
32674                                 cls: "roo-layout-split roo-layout-split-"+this.position,
32675                                 html: "&#160;"
32676                 });
32677                 /** The SplitBar for this region 
32678                 * @type Roo.SplitBar */
32679                 // does not exist yet...
32680                 Roo.log([this.position, this.orientation]);
32681                 
32682                 this.split = new Roo.bootstrap.SplitBar({
32683                     dragElement : splitEl,
32684                     resizingElement: this.el,
32685                     orientation : this.orientation
32686                 });
32687                 
32688                 this.split.on("moved", this.onSplitMove, this);
32689                 this.split.useShim = config.useShim === true;
32690                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32691                 if(this.useSplitTips){
32692                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32693                 }
32694                 //if(config.collapsible){
32695                 //    this.split.el.on("dblclick", this.collapse,  this);
32696                 //}
32697             }
32698             if(typeof config.minSize != "undefined"){
32699                 this.split.minSize = config.minSize;
32700             }
32701             if(typeof config.maxSize != "undefined"){
32702                 this.split.maxSize = config.maxSize;
32703             }
32704             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32705                 this.hideSplitter();
32706             }
32707         }
32708     },
32709
32710     getHMaxSize : function(){
32711          var cmax = this.config.maxSize || 10000;
32712          var center = this.mgr.getRegion("center");
32713          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32714     },
32715
32716     getVMaxSize : function(){
32717          var cmax = this.config.maxSize || 10000;
32718          var center = this.mgr.getRegion("center");
32719          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32720     },
32721
32722     onSplitMove : function(split, newSize){
32723         this.fireEvent("resized", this, newSize);
32724     },
32725     
32726     /** 
32727      * Returns the {@link Roo.SplitBar} for this region.
32728      * @return {Roo.SplitBar}
32729      */
32730     getSplitBar : function(){
32731         return this.split;
32732     },
32733     
32734     hide : function(){
32735         this.hideSplitter();
32736         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32737     },
32738
32739     hideSplitter : function(){
32740         if(this.split){
32741             this.split.el.setLocation(-2000,-2000);
32742             this.split.el.hide();
32743         }
32744     },
32745
32746     show : function(){
32747         if(this.split){
32748             this.split.el.show();
32749         }
32750         Roo.bootstrap.layout.Split.superclass.show.call(this);
32751     },
32752     
32753     beforeSlide: function(){
32754         if(Roo.isGecko){// firefox overflow auto bug workaround
32755             this.bodyEl.clip();
32756             if(this.tabs) {
32757                 this.tabs.bodyEl.clip();
32758             }
32759             if(this.activePanel){
32760                 this.activePanel.getEl().clip();
32761                 
32762                 if(this.activePanel.beforeSlide){
32763                     this.activePanel.beforeSlide();
32764                 }
32765             }
32766         }
32767     },
32768     
32769     afterSlide : function(){
32770         if(Roo.isGecko){// firefox overflow auto bug workaround
32771             this.bodyEl.unclip();
32772             if(this.tabs) {
32773                 this.tabs.bodyEl.unclip();
32774             }
32775             if(this.activePanel){
32776                 this.activePanel.getEl().unclip();
32777                 if(this.activePanel.afterSlide){
32778                     this.activePanel.afterSlide();
32779                 }
32780             }
32781         }
32782     },
32783
32784     initAutoHide : function(){
32785         if(this.autoHide !== false){
32786             if(!this.autoHideHd){
32787                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32788                 this.autoHideHd = {
32789                     "mouseout": function(e){
32790                         if(!e.within(this.el, true)){
32791                             st.delay(500);
32792                         }
32793                     },
32794                     "mouseover" : function(e){
32795                         st.cancel();
32796                     },
32797                     scope : this
32798                 };
32799             }
32800             this.el.on(this.autoHideHd);
32801         }
32802     },
32803
32804     clearAutoHide : function(){
32805         if(this.autoHide !== false){
32806             this.el.un("mouseout", this.autoHideHd.mouseout);
32807             this.el.un("mouseover", this.autoHideHd.mouseover);
32808         }
32809     },
32810
32811     clearMonitor : function(){
32812         Roo.get(document).un("click", this.slideInIf, this);
32813     },
32814
32815     // these names are backwards but not changed for compat
32816     slideOut : function(){
32817         if(this.isSlid || this.el.hasActiveFx()){
32818             return;
32819         }
32820         this.isSlid = true;
32821         if(this.collapseBtn){
32822             this.collapseBtn.hide();
32823         }
32824         this.closeBtnState = this.closeBtn.getStyle('display');
32825         this.closeBtn.hide();
32826         if(this.stickBtn){
32827             this.stickBtn.show();
32828         }
32829         this.el.show();
32830         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32831         this.beforeSlide();
32832         this.el.setStyle("z-index", 10001);
32833         this.el.slideIn(this.getSlideAnchor(), {
32834             callback: function(){
32835                 this.afterSlide();
32836                 this.initAutoHide();
32837                 Roo.get(document).on("click", this.slideInIf, this);
32838                 this.fireEvent("slideshow", this);
32839             },
32840             scope: this,
32841             block: true
32842         });
32843     },
32844
32845     afterSlideIn : function(){
32846         this.clearAutoHide();
32847         this.isSlid = false;
32848         this.clearMonitor();
32849         this.el.setStyle("z-index", "");
32850         if(this.collapseBtn){
32851             this.collapseBtn.show();
32852         }
32853         this.closeBtn.setStyle('display', this.closeBtnState);
32854         if(this.stickBtn){
32855             this.stickBtn.hide();
32856         }
32857         this.fireEvent("slidehide", this);
32858     },
32859
32860     slideIn : function(cb){
32861         if(!this.isSlid || this.el.hasActiveFx()){
32862             Roo.callback(cb);
32863             return;
32864         }
32865         this.isSlid = false;
32866         this.beforeSlide();
32867         this.el.slideOut(this.getSlideAnchor(), {
32868             callback: function(){
32869                 this.el.setLeftTop(-10000, -10000);
32870                 this.afterSlide();
32871                 this.afterSlideIn();
32872                 Roo.callback(cb);
32873             },
32874             scope: this,
32875             block: true
32876         });
32877     },
32878     
32879     slideInIf : function(e){
32880         if(!e.within(this.el)){
32881             this.slideIn();
32882         }
32883     },
32884
32885     animateCollapse : function(){
32886         this.beforeSlide();
32887         this.el.setStyle("z-index", 20000);
32888         var anchor = this.getSlideAnchor();
32889         this.el.slideOut(anchor, {
32890             callback : function(){
32891                 this.el.setStyle("z-index", "");
32892                 this.collapsedEl.slideIn(anchor, {duration:.3});
32893                 this.afterSlide();
32894                 this.el.setLocation(-10000,-10000);
32895                 this.el.hide();
32896                 this.fireEvent("collapsed", this);
32897             },
32898             scope: this,
32899             block: true
32900         });
32901     },
32902
32903     animateExpand : function(){
32904         this.beforeSlide();
32905         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32906         this.el.setStyle("z-index", 20000);
32907         this.collapsedEl.hide({
32908             duration:.1
32909         });
32910         this.el.slideIn(this.getSlideAnchor(), {
32911             callback : function(){
32912                 this.el.setStyle("z-index", "");
32913                 this.afterSlide();
32914                 if(this.split){
32915                     this.split.el.show();
32916                 }
32917                 this.fireEvent("invalidated", this);
32918                 this.fireEvent("expanded", this);
32919             },
32920             scope: this,
32921             block: true
32922         });
32923     },
32924
32925     anchors : {
32926         "west" : "left",
32927         "east" : "right",
32928         "north" : "top",
32929         "south" : "bottom"
32930     },
32931
32932     sanchors : {
32933         "west" : "l",
32934         "east" : "r",
32935         "north" : "t",
32936         "south" : "b"
32937     },
32938
32939     canchors : {
32940         "west" : "tl-tr",
32941         "east" : "tr-tl",
32942         "north" : "tl-bl",
32943         "south" : "bl-tl"
32944     },
32945
32946     getAnchor : function(){
32947         return this.anchors[this.position];
32948     },
32949
32950     getCollapseAnchor : function(){
32951         return this.canchors[this.position];
32952     },
32953
32954     getSlideAnchor : function(){
32955         return this.sanchors[this.position];
32956     },
32957
32958     getAlignAdj : function(){
32959         var cm = this.cmargins;
32960         switch(this.position){
32961             case "west":
32962                 return [0, 0];
32963             break;
32964             case "east":
32965                 return [0, 0];
32966             break;
32967             case "north":
32968                 return [0, 0];
32969             break;
32970             case "south":
32971                 return [0, 0];
32972             break;
32973         }
32974     },
32975
32976     getExpandAdj : function(){
32977         var c = this.collapsedEl, cm = this.cmargins;
32978         switch(this.position){
32979             case "west":
32980                 return [-(cm.right+c.getWidth()+cm.left), 0];
32981             break;
32982             case "east":
32983                 return [cm.right+c.getWidth()+cm.left, 0];
32984             break;
32985             case "north":
32986                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32987             break;
32988             case "south":
32989                 return [0, cm.top+cm.bottom+c.getHeight()];
32990             break;
32991         }
32992     }
32993 });/*
32994  * Based on:
32995  * Ext JS Library 1.1.1
32996  * Copyright(c) 2006-2007, Ext JS, LLC.
32997  *
32998  * Originally Released Under LGPL - original licence link has changed is not relivant.
32999  *
33000  * Fork - LGPL
33001  * <script type="text/javascript">
33002  */
33003 /*
33004  * These classes are private internal classes
33005  */
33006 Roo.bootstrap.layout.Center = function(config){
33007     config.region = "center";
33008     Roo.bootstrap.layout.Region.call(this, config);
33009     this.visible = true;
33010     this.minWidth = config.minWidth || 20;
33011     this.minHeight = config.minHeight || 20;
33012 };
33013
33014 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33015     hide : function(){
33016         // center panel can't be hidden
33017     },
33018     
33019     show : function(){
33020         // center panel can't be hidden
33021     },
33022     
33023     getMinWidth: function(){
33024         return this.minWidth;
33025     },
33026     
33027     getMinHeight: function(){
33028         return this.minHeight;
33029     }
33030 });
33031
33032
33033
33034
33035  
33036
33037
33038
33039
33040
33041 Roo.bootstrap.layout.North = function(config)
33042 {
33043     config.region = 'north';
33044     config.cursor = 'n-resize';
33045     
33046     Roo.bootstrap.layout.Split.call(this, config);
33047     if(this.split){
33048         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33049         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33050         this.split.el.addClass("roo-layout-split-v");
33051     }
33052     var size = config.initialSize || config.height;
33053     if(typeof size != "undefined"){
33054         this.el.setHeight(size);
33055     }
33056 };
33057 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33058 {
33059     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33060     getBox : function(){
33061         if(this.collapsed){
33062             return this.collapsedEl.getBox();
33063         }
33064         var box = this.el.getBox();
33065         if(this.split){
33066             box.height += this.split.el.getHeight();
33067         }
33068         return box;
33069     },
33070     
33071     updateBox : function(box){
33072         if(this.split && !this.collapsed){
33073             box.height -= this.split.el.getHeight();
33074             this.split.el.setLeft(box.x);
33075             this.split.el.setTop(box.y+box.height);
33076             this.split.el.setWidth(box.width);
33077         }
33078         if(this.collapsed){
33079             this.updateBody(box.width, null);
33080         }
33081         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33082     }
33083 });
33084
33085
33086
33087
33088
33089 Roo.bootstrap.layout.South = function(config){
33090     config.region = 'south';
33091     config.cursor = 's-resize';
33092     Roo.bootstrap.layout.Split.call(this, config);
33093     if(this.split){
33094         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33095         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33096         this.split.el.addClass("roo-layout-split-v");
33097     }
33098     var size = config.initialSize || config.height;
33099     if(typeof size != "undefined"){
33100         this.el.setHeight(size);
33101     }
33102 };
33103
33104 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33105     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33106     getBox : function(){
33107         if(this.collapsed){
33108             return this.collapsedEl.getBox();
33109         }
33110         var box = this.el.getBox();
33111         if(this.split){
33112             var sh = this.split.el.getHeight();
33113             box.height += sh;
33114             box.y -= sh;
33115         }
33116         return box;
33117     },
33118     
33119     updateBox : function(box){
33120         if(this.split && !this.collapsed){
33121             var sh = this.split.el.getHeight();
33122             box.height -= sh;
33123             box.y += sh;
33124             this.split.el.setLeft(box.x);
33125             this.split.el.setTop(box.y-sh);
33126             this.split.el.setWidth(box.width);
33127         }
33128         if(this.collapsed){
33129             this.updateBody(box.width, null);
33130         }
33131         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33132     }
33133 });
33134
33135 Roo.bootstrap.layout.East = function(config){
33136     config.region = "east";
33137     config.cursor = "e-resize";
33138     Roo.bootstrap.layout.Split.call(this, config);
33139     if(this.split){
33140         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33141         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33142         this.split.el.addClass("roo-layout-split-h");
33143     }
33144     var size = config.initialSize || config.width;
33145     if(typeof size != "undefined"){
33146         this.el.setWidth(size);
33147     }
33148 };
33149 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33150     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33151     getBox : function(){
33152         if(this.collapsed){
33153             return this.collapsedEl.getBox();
33154         }
33155         var box = this.el.getBox();
33156         if(this.split){
33157             var sw = this.split.el.getWidth();
33158             box.width += sw;
33159             box.x -= sw;
33160         }
33161         return box;
33162     },
33163
33164     updateBox : function(box){
33165         if(this.split && !this.collapsed){
33166             var sw = this.split.el.getWidth();
33167             box.width -= sw;
33168             this.split.el.setLeft(box.x);
33169             this.split.el.setTop(box.y);
33170             this.split.el.setHeight(box.height);
33171             box.x += sw;
33172         }
33173         if(this.collapsed){
33174             this.updateBody(null, box.height);
33175         }
33176         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33177     }
33178 });
33179
33180 Roo.bootstrap.layout.West = function(config){
33181     config.region = "west";
33182     config.cursor = "w-resize";
33183     
33184     Roo.bootstrap.layout.Split.call(this, config);
33185     if(this.split){
33186         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33187         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33188         this.split.el.addClass("roo-layout-split-h");
33189     }
33190     var size = config.initialSize || config.width;
33191     if(typeof size != "undefined"){
33192         this.el.setWidth(size);
33193     }
33194 };
33195 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33196     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33197     getBox : function(){
33198         if(this.collapsed){
33199             return this.collapsedEl.getBox();
33200         }
33201         var box = this.el.getBox();
33202         if(this.split){
33203             box.width += this.split.el.getWidth();
33204         }
33205         return box;
33206     },
33207     
33208     updateBox : function(box){
33209         if(this.split && !this.collapsed){
33210             var sw = this.split.el.getWidth();
33211             box.width -= sw;
33212             this.split.el.setLeft(box.x+box.width);
33213             this.split.el.setTop(box.y);
33214             this.split.el.setHeight(box.height);
33215         }
33216         if(this.collapsed){
33217             this.updateBody(null, box.height);
33218         }
33219         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33220     }
33221 });
33222 Roo.namespace("Roo.bootstrap.panel");/*
33223  * Based on:
33224  * Ext JS Library 1.1.1
33225  * Copyright(c) 2006-2007, Ext JS, LLC.
33226  *
33227  * Originally Released Under LGPL - original licence link has changed is not relivant.
33228  *
33229  * Fork - LGPL
33230  * <script type="text/javascript">
33231  */
33232 /**
33233  * @class Roo.ContentPanel
33234  * @extends Roo.util.Observable
33235  * A basic ContentPanel element.
33236  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33237  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33238  * @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
33239  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33240  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33241  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33242  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33243  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33244  * @cfg {String} title          The title for this panel
33245  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33246  * @cfg {String} url            Calls {@link #setUrl} with this value
33247  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33248  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33249  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33250  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33251
33252  * @constructor
33253  * Create a new ContentPanel.
33254  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33255  * @param {String/Object} config A string to set only the title or a config object
33256  * @param {String} content (optional) Set the HTML content for this panel
33257  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33258  */
33259 Roo.bootstrap.panel.Content = function( config){
33260     
33261     var el = config.el;
33262     var content = config.content;
33263
33264     if(config.autoCreate){ // xtype is available if this is called from factory
33265         el = Roo.id();
33266     }
33267     this.el = Roo.get(el);
33268     if(!this.el && config && config.autoCreate){
33269         if(typeof config.autoCreate == "object"){
33270             if(!config.autoCreate.id){
33271                 config.autoCreate.id = config.id||el;
33272             }
33273             this.el = Roo.DomHelper.append(document.body,
33274                         config.autoCreate, true);
33275         }else{
33276             var elcfg =  {   tag: "div",
33277                             cls: "roo-layout-inactive-content",
33278                             id: config.id||el
33279                             };
33280             if (config.html) {
33281                 elcfg.html = config.html;
33282                 
33283             }
33284                         
33285             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33286         }
33287     } 
33288     this.closable = false;
33289     this.loaded = false;
33290     this.active = false;
33291     if(typeof config == "string"){
33292         this.title = config;
33293     }else{
33294         Roo.apply(this, config);
33295     }
33296     /*
33297     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33298         this.wrapEl = this.el.wrap();
33299         this.toolbar.container = this.el.insertSibling(false, 'before');
33300         this.toolbar = new Roo.Toolbar(this.toolbar);
33301     }
33302     
33303     // xtype created footer. - not sure if will work as we normally have to render first..
33304     if (this.footer && !this.footer.el && this.footer.xtype) {
33305         if (!this.wrapEl) {
33306             this.wrapEl = this.el.wrap();
33307         }
33308     
33309         this.footer.container = this.wrapEl.createChild();
33310          
33311         this.footer = Roo.factory(this.footer, Roo);
33312         
33313     }
33314     */
33315     if(this.resizeEl){
33316         this.resizeEl = Roo.get(this.resizeEl, true);
33317     }else{
33318         this.resizeEl = this.el;
33319     }
33320     // handle view.xtype
33321     
33322  
33323     
33324     
33325     this.addEvents({
33326         /**
33327          * @event activate
33328          * Fires when this panel is activated. 
33329          * @param {Roo.ContentPanel} this
33330          */
33331         "activate" : true,
33332         /**
33333          * @event deactivate
33334          * Fires when this panel is activated. 
33335          * @param {Roo.ContentPanel} this
33336          */
33337         "deactivate" : true,
33338
33339         /**
33340          * @event resize
33341          * Fires when this panel is resized if fitToFrame is true.
33342          * @param {Roo.ContentPanel} this
33343          * @param {Number} width The width after any component adjustments
33344          * @param {Number} height The height after any component adjustments
33345          */
33346         "resize" : true,
33347         
33348          /**
33349          * @event render
33350          * Fires when this tab is created
33351          * @param {Roo.ContentPanel} this
33352          */
33353         "render" : true
33354         
33355         
33356         
33357     });
33358     
33359
33360     
33361     
33362     if(this.autoScroll){
33363         this.resizeEl.setStyle("overflow", "auto");
33364     } else {
33365         // fix randome scrolling
33366         this.el.on('scroll', function() {
33367             Roo.log('fix random scolling');
33368             this.scrollTo('top',0); 
33369         });
33370     }
33371     content = content || this.content;
33372     if(content){
33373         this.setContent(content);
33374     }
33375     if(config && config.url){
33376         this.setUrl(this.url, this.params, this.loadOnce);
33377     }
33378     
33379     
33380     
33381     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33382     
33383     if (this.view && typeof(this.view.xtype) != 'undefined') {
33384         this.view.el = this.el.appendChild(document.createElement("div"));
33385         this.view = Roo.factory(this.view); 
33386         this.view.render  &&  this.view.render(false, '');  
33387     }
33388     
33389     
33390     this.fireEvent('render', this);
33391 };
33392
33393 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33394     tabTip:'',
33395     setRegion : function(region){
33396         this.region = region;
33397         if(region){
33398            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33399         }else{
33400            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33401         } 
33402     },
33403     
33404     /**
33405      * Returns the toolbar for this Panel if one was configured. 
33406      * @return {Roo.Toolbar} 
33407      */
33408     getToolbar : function(){
33409         return this.toolbar;
33410     },
33411     
33412     setActiveState : function(active){
33413         this.active = active;
33414         if(!active){
33415             this.fireEvent("deactivate", this);
33416         }else{
33417             this.fireEvent("activate", this);
33418         }
33419     },
33420     /**
33421      * Updates this panel's element
33422      * @param {String} content The new content
33423      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33424     */
33425     setContent : function(content, loadScripts){
33426         this.el.update(content, loadScripts);
33427     },
33428
33429     ignoreResize : function(w, h){
33430         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33431             return true;
33432         }else{
33433             this.lastSize = {width: w, height: h};
33434             return false;
33435         }
33436     },
33437     /**
33438      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33439      * @return {Roo.UpdateManager} The UpdateManager
33440      */
33441     getUpdateManager : function(){
33442         return this.el.getUpdateManager();
33443     },
33444      /**
33445      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33446      * @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:
33447 <pre><code>
33448 panel.load({
33449     url: "your-url.php",
33450     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33451     callback: yourFunction,
33452     scope: yourObject, //(optional scope)
33453     discardUrl: false,
33454     nocache: false,
33455     text: "Loading...",
33456     timeout: 30,
33457     scripts: false
33458 });
33459 </code></pre>
33460      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33461      * 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.
33462      * @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}
33463      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33464      * @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.
33465      * @return {Roo.ContentPanel} this
33466      */
33467     load : function(){
33468         var um = this.el.getUpdateManager();
33469         um.update.apply(um, arguments);
33470         return this;
33471     },
33472
33473
33474     /**
33475      * 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.
33476      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33477      * @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)
33478      * @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)
33479      * @return {Roo.UpdateManager} The UpdateManager
33480      */
33481     setUrl : function(url, params, loadOnce){
33482         if(this.refreshDelegate){
33483             this.removeListener("activate", this.refreshDelegate);
33484         }
33485         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33486         this.on("activate", this.refreshDelegate);
33487         return this.el.getUpdateManager();
33488     },
33489     
33490     _handleRefresh : function(url, params, loadOnce){
33491         if(!loadOnce || !this.loaded){
33492             var updater = this.el.getUpdateManager();
33493             updater.update(url, params, this._setLoaded.createDelegate(this));
33494         }
33495     },
33496     
33497     _setLoaded : function(){
33498         this.loaded = true;
33499     }, 
33500     
33501     /**
33502      * Returns this panel's id
33503      * @return {String} 
33504      */
33505     getId : function(){
33506         return this.el.id;
33507     },
33508     
33509     /** 
33510      * Returns this panel's element - used by regiosn to add.
33511      * @return {Roo.Element} 
33512      */
33513     getEl : function(){
33514         return this.wrapEl || this.el;
33515     },
33516     
33517    
33518     
33519     adjustForComponents : function(width, height)
33520     {
33521         //Roo.log('adjustForComponents ');
33522         if(this.resizeEl != this.el){
33523             width -= this.el.getFrameWidth('lr');
33524             height -= this.el.getFrameWidth('tb');
33525         }
33526         if(this.toolbar){
33527             var te = this.toolbar.getEl();
33528             height -= te.getHeight();
33529             te.setWidth(width);
33530         }
33531         if(this.footer){
33532             var te = this.footer.getEl();
33533             Roo.log("footer:" + te.getHeight());
33534             
33535             height -= te.getHeight();
33536             te.setWidth(width);
33537         }
33538         
33539         
33540         if(this.adjustments){
33541             width += this.adjustments[0];
33542             height += this.adjustments[1];
33543         }
33544         return {"width": width, "height": height};
33545     },
33546     
33547     setSize : function(width, height){
33548         if(this.fitToFrame && !this.ignoreResize(width, height)){
33549             if(this.fitContainer && this.resizeEl != this.el){
33550                 this.el.setSize(width, height);
33551             }
33552             var size = this.adjustForComponents(width, height);
33553             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33554             this.fireEvent('resize', this, size.width, size.height);
33555         }
33556     },
33557     
33558     /**
33559      * Returns this panel's title
33560      * @return {String} 
33561      */
33562     getTitle : function(){
33563         return this.title;
33564     },
33565     
33566     /**
33567      * Set this panel's title
33568      * @param {String} title
33569      */
33570     setTitle : function(title){
33571         this.title = title;
33572         if(this.region){
33573             this.region.updatePanelTitle(this, title);
33574         }
33575     },
33576     
33577     /**
33578      * Returns true is this panel was configured to be closable
33579      * @return {Boolean} 
33580      */
33581     isClosable : function(){
33582         return this.closable;
33583     },
33584     
33585     beforeSlide : function(){
33586         this.el.clip();
33587         this.resizeEl.clip();
33588     },
33589     
33590     afterSlide : function(){
33591         this.el.unclip();
33592         this.resizeEl.unclip();
33593     },
33594     
33595     /**
33596      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33597      *   Will fail silently if the {@link #setUrl} method has not been called.
33598      *   This does not activate the panel, just updates its content.
33599      */
33600     refresh : function(){
33601         if(this.refreshDelegate){
33602            this.loaded = false;
33603            this.refreshDelegate();
33604         }
33605     },
33606     
33607     /**
33608      * Destroys this panel
33609      */
33610     destroy : function(){
33611         this.el.removeAllListeners();
33612         var tempEl = document.createElement("span");
33613         tempEl.appendChild(this.el.dom);
33614         tempEl.innerHTML = "";
33615         this.el.remove();
33616         this.el = null;
33617     },
33618     
33619     /**
33620      * form - if the content panel contains a form - this is a reference to it.
33621      * @type {Roo.form.Form}
33622      */
33623     form : false,
33624     /**
33625      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33626      *    This contains a reference to it.
33627      * @type {Roo.View}
33628      */
33629     view : false,
33630     
33631       /**
33632      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33633      * <pre><code>
33634
33635 layout.addxtype({
33636        xtype : 'Form',
33637        items: [ .... ]
33638    }
33639 );
33640
33641 </code></pre>
33642      * @param {Object} cfg Xtype definition of item to add.
33643      */
33644     
33645     
33646     getChildContainer: function () {
33647         return this.getEl();
33648     }
33649     
33650     
33651     /*
33652         var  ret = new Roo.factory(cfg);
33653         return ret;
33654         
33655         
33656         // add form..
33657         if (cfg.xtype.match(/^Form$/)) {
33658             
33659             var el;
33660             //if (this.footer) {
33661             //    el = this.footer.container.insertSibling(false, 'before');
33662             //} else {
33663                 el = this.el.createChild();
33664             //}
33665
33666             this.form = new  Roo.form.Form(cfg);
33667             
33668             
33669             if ( this.form.allItems.length) {
33670                 this.form.render(el.dom);
33671             }
33672             return this.form;
33673         }
33674         // should only have one of theses..
33675         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33676             // views.. should not be just added - used named prop 'view''
33677             
33678             cfg.el = this.el.appendChild(document.createElement("div"));
33679             // factory?
33680             
33681             var ret = new Roo.factory(cfg);
33682              
33683              ret.render && ret.render(false, ''); // render blank..
33684             this.view = ret;
33685             return ret;
33686         }
33687         return false;
33688     }
33689     \*/
33690 });
33691  
33692 /**
33693  * @class Roo.bootstrap.panel.Grid
33694  * @extends Roo.bootstrap.panel.Content
33695  * @constructor
33696  * Create a new GridPanel.
33697  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33698  * @param {String/Object} config A string to set only the panel's title, or a config object
33699
33700   new Roo.bootstrap.panel.Grid({
33701                 grid: .....
33702                 ....
33703   }
33704
33705  */
33706
33707
33708
33709 Roo.bootstrap.panel.Grid = function(config){
33710     
33711   
33712     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33713         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33714         
33715     this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33716     config.el = this.wrapper;
33717     
33718     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33719     
33720     if(this.toolbar){
33721         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33722     }
33723     // xtype created footer. - not sure if will work as we normally have to render first..
33724     if (this.footer && !this.footer.el && this.footer.xtype) {
33725         
33726         this.footer.container = this.grid.getView().getFooterPanel(true);
33727         this.footer.dataSource = this.grid.dataSource;
33728         this.footer = Roo.factory(this.footer, Roo);
33729         
33730     }
33731     
33732     
33733     config.grid.monitorWindowResize = false; // turn off autosizing
33734     config.grid.autoHeight = false;
33735     config.grid.autoWidth = false;
33736     this.grid = config.grid;
33737     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33738     
33739      
33740 };
33741
33742 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33743     getId : function(){
33744         return this.grid.id;
33745     },
33746     
33747     /**
33748      * Returns the grid for this panel
33749      * @return {Roo.bootstrap.Table} 
33750      */
33751     getGrid : function(){
33752         return this.grid;    
33753     },
33754     
33755     setSize : function(width, height){
33756         if(!this.ignoreResize(width, height)){
33757             var grid = this.grid;
33758             var size = this.adjustForComponents(width, height);
33759             grid.getGridEl().setSize(size.width, size.height);
33760             /*
33761             var thd = grid.getGridEl().select('thead',true).first();
33762             var tbd = grid.getGridEl().select('tbody', true).first();
33763             if (tbd) {
33764                 tbd.setSize(width, height - thd.getHeight());
33765             }
33766             */
33767             grid.autoSize();
33768         }
33769     },
33770      
33771     
33772     
33773     beforeSlide : function(){
33774         this.grid.getView().scroller.clip();
33775     },
33776     
33777     afterSlide : function(){
33778         this.grid.getView().scroller.unclip();
33779     },
33780     
33781     destroy : function(){
33782         this.grid.destroy();
33783         delete this.grid;
33784         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
33785     }
33786 });
33787
33788 /**
33789  * @class Roo.bootstrap.panel.Nest
33790  * @extends Roo.bootstrap.panel.Content
33791  * @constructor
33792  * Create a new Panel, that can contain a layout.Border.
33793  * 
33794  * 
33795  * @param {Roo.BorderLayout} layout The layout for this panel
33796  * @param {String/Object} config A string to set only the title or a config object
33797  */
33798 Roo.bootstrap.panel.Nest = function(config)
33799 {
33800     // construct with only one argument..
33801     /* FIXME - implement nicer consturctors
33802     if (layout.layout) {
33803         config = layout;
33804         layout = config.layout;
33805         delete config.layout;
33806     }
33807     if (layout.xtype && !layout.getEl) {
33808         // then layout needs constructing..
33809         layout = Roo.factory(layout, Roo);
33810     }
33811     */
33812     
33813     config.el =  config.layout.getEl();
33814     
33815     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
33816     
33817     config.layout.monitorWindowResize = false; // turn off autosizing
33818     this.layout = config.layout;
33819     this.layout.getEl().addClass("roo-layout-nested-layout");
33820     
33821     
33822     
33823     
33824 };
33825
33826 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
33827
33828     setSize : function(width, height){
33829         if(!this.ignoreResize(width, height)){
33830             var size = this.adjustForComponents(width, height);
33831             var el = this.layout.getEl();
33832             el.setSize(size.width, size.height);
33833             var touch = el.dom.offsetWidth;
33834             this.layout.layout();
33835             // ie requires a double layout on the first pass
33836             if(Roo.isIE && !this.initialized){
33837                 this.initialized = true;
33838                 this.layout.layout();
33839             }
33840         }
33841     },
33842     
33843     // activate all subpanels if not currently active..
33844     
33845     setActiveState : function(active){
33846         this.active = active;
33847         if(!active){
33848             this.fireEvent("deactivate", this);
33849             return;
33850         }
33851         
33852         this.fireEvent("activate", this);
33853         // not sure if this should happen before or after..
33854         if (!this.layout) {
33855             return; // should not happen..
33856         }
33857         var reg = false;
33858         for (var r in this.layout.regions) {
33859             reg = this.layout.getRegion(r);
33860             if (reg.getActivePanel()) {
33861                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33862                 reg.setActivePanel(reg.getActivePanel());
33863                 continue;
33864             }
33865             if (!reg.panels.length) {
33866                 continue;
33867             }
33868             reg.showPanel(reg.getPanel(0));
33869         }
33870         
33871         
33872         
33873         
33874     },
33875     
33876     /**
33877      * Returns the nested BorderLayout for this panel
33878      * @return {Roo.BorderLayout} 
33879      */
33880     getLayout : function(){
33881         return this.layout;
33882     },
33883     
33884      /**
33885      * Adds a xtype elements to the layout of the nested panel
33886      * <pre><code>
33887
33888 panel.addxtype({
33889        xtype : 'ContentPanel',
33890        region: 'west',
33891        items: [ .... ]
33892    }
33893 );
33894
33895 panel.addxtype({
33896         xtype : 'NestedLayoutPanel',
33897         region: 'west',
33898         layout: {
33899            center: { },
33900            west: { }   
33901         },
33902         items : [ ... list of content panels or nested layout panels.. ]
33903    }
33904 );
33905 </code></pre>
33906      * @param {Object} cfg Xtype definition of item to add.
33907      */
33908     addxtype : function(cfg) {
33909         return this.layout.addxtype(cfg);
33910     
33911     }
33912 });        /*
33913  * Based on:
33914  * Ext JS Library 1.1.1
33915  * Copyright(c) 2006-2007, Ext JS, LLC.
33916  *
33917  * Originally Released Under LGPL - original licence link has changed is not relivant.
33918  *
33919  * Fork - LGPL
33920  * <script type="text/javascript">
33921  */
33922 /**
33923  * @class Roo.TabPanel
33924  * @extends Roo.util.Observable
33925  * A lightweight tab container.
33926  * <br><br>
33927  * Usage:
33928  * <pre><code>
33929 // basic tabs 1, built from existing content
33930 var tabs = new Roo.TabPanel("tabs1");
33931 tabs.addTab("script", "View Script");
33932 tabs.addTab("markup", "View Markup");
33933 tabs.activate("script");
33934
33935 // more advanced tabs, built from javascript
33936 var jtabs = new Roo.TabPanel("jtabs");
33937 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
33938
33939 // set up the UpdateManager
33940 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
33941 var updater = tab2.getUpdateManager();
33942 updater.setDefaultUrl("ajax1.htm");
33943 tab2.on('activate', updater.refresh, updater, true);
33944
33945 // Use setUrl for Ajax loading
33946 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
33947 tab3.setUrl("ajax2.htm", null, true);
33948
33949 // Disabled tab
33950 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
33951 tab4.disable();
33952
33953 jtabs.activate("jtabs-1");
33954  * </code></pre>
33955  * @constructor
33956  * Create a new TabPanel.
33957  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
33958  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
33959  */
33960 Roo.bootstrap.panel.Tabs = function(config){
33961     /**
33962     * The container element for this TabPanel.
33963     * @type Roo.Element
33964     */
33965     this.el = Roo.get(config.el);
33966     delete config.el;
33967     if(config){
33968         if(typeof config == "boolean"){
33969             this.tabPosition = config ? "bottom" : "top";
33970         }else{
33971             Roo.apply(this, config);
33972         }
33973     }
33974     
33975     if(this.tabPosition == "bottom"){
33976         this.bodyEl = Roo.get(this.createBody(this.el.dom));
33977         this.el.addClass("roo-tabs-bottom");
33978     }
33979     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
33980     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
33981     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
33982     if(Roo.isIE){
33983         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
33984     }
33985     if(this.tabPosition != "bottom"){
33986         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
33987          * @type Roo.Element
33988          */
33989         this.bodyEl = Roo.get(this.createBody(this.el.dom));
33990         this.el.addClass("roo-tabs-top");
33991     }
33992     this.items = [];
33993
33994     this.bodyEl.setStyle("position", "relative");
33995
33996     this.active = null;
33997     this.activateDelegate = this.activate.createDelegate(this);
33998
33999     this.addEvents({
34000         /**
34001          * @event tabchange
34002          * Fires when the active tab changes
34003          * @param {Roo.TabPanel} this
34004          * @param {Roo.TabPanelItem} activePanel The new active tab
34005          */
34006         "tabchange": true,
34007         /**
34008          * @event beforetabchange
34009          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34010          * @param {Roo.TabPanel} this
34011          * @param {Object} e Set cancel to true on this object to cancel the tab change
34012          * @param {Roo.TabPanelItem} tab The tab being changed to
34013          */
34014         "beforetabchange" : true
34015     });
34016
34017     Roo.EventManager.onWindowResize(this.onResize, this);
34018     this.cpad = this.el.getPadding("lr");
34019     this.hiddenCount = 0;
34020
34021
34022     // toolbar on the tabbar support...
34023     if (this.toolbar) {
34024         alert("no toolbar support yet");
34025         this.toolbar  = false;
34026         /*
34027         var tcfg = this.toolbar;
34028         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34029         this.toolbar = new Roo.Toolbar(tcfg);
34030         if (Roo.isSafari) {
34031             var tbl = tcfg.container.child('table', true);
34032             tbl.setAttribute('width', '100%');
34033         }
34034         */
34035         
34036     }
34037    
34038
34039
34040     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34041 };
34042
34043 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34044     /*
34045      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34046      */
34047     tabPosition : "top",
34048     /*
34049      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34050      */
34051     currentTabWidth : 0,
34052     /*
34053      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34054      */
34055     minTabWidth : 40,
34056     /*
34057      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34058      */
34059     maxTabWidth : 250,
34060     /*
34061      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34062      */
34063     preferredTabWidth : 175,
34064     /*
34065      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34066      */
34067     resizeTabs : false,
34068     /*
34069      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34070      */
34071     monitorResize : true,
34072     /*
34073      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34074      */
34075     toolbar : false,
34076
34077     /**
34078      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34079      * @param {String} id The id of the div to use <b>or create</b>
34080      * @param {String} text The text for the tab
34081      * @param {String} content (optional) Content to put in the TabPanelItem body
34082      * @param {Boolean} closable (optional) True to create a close icon on the tab
34083      * @return {Roo.TabPanelItem} The created TabPanelItem
34084      */
34085     addTab : function(id, text, content, closable)
34086     {
34087         var item = new Roo.bootstrap.panel.TabItem({
34088             panel: this,
34089             id : id,
34090             text : text,
34091             closable : closable
34092         });
34093         this.addTabItem(item);
34094         if(content){
34095             item.setContent(content);
34096         }
34097         return item;
34098     },
34099
34100     /**
34101      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34102      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34103      * @return {Roo.TabPanelItem}
34104      */
34105     getTab : function(id){
34106         return this.items[id];
34107     },
34108
34109     /**
34110      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34111      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34112      */
34113     hideTab : function(id){
34114         var t = this.items[id];
34115         if(!t.isHidden()){
34116            t.setHidden(true);
34117            this.hiddenCount++;
34118            this.autoSizeTabs();
34119         }
34120     },
34121
34122     /**
34123      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34124      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34125      */
34126     unhideTab : function(id){
34127         var t = this.items[id];
34128         if(t.isHidden()){
34129            t.setHidden(false);
34130            this.hiddenCount--;
34131            this.autoSizeTabs();
34132         }
34133     },
34134
34135     /**
34136      * Adds an existing {@link Roo.TabPanelItem}.
34137      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34138      */
34139     addTabItem : function(item){
34140         this.items[item.id] = item;
34141         this.items.push(item);
34142       //  if(this.resizeTabs){
34143     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34144   //         this.autoSizeTabs();
34145 //        }else{
34146 //            item.autoSize();
34147        // }
34148     },
34149
34150     /**
34151      * Removes a {@link Roo.TabPanelItem}.
34152      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34153      */
34154     removeTab : function(id){
34155         var items = this.items;
34156         var tab = items[id];
34157         if(!tab) { return; }
34158         var index = items.indexOf(tab);
34159         if(this.active == tab && items.length > 1){
34160             var newTab = this.getNextAvailable(index);
34161             if(newTab) {
34162                 newTab.activate();
34163             }
34164         }
34165         this.stripEl.dom.removeChild(tab.pnode.dom);
34166         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34167             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34168         }
34169         items.splice(index, 1);
34170         delete this.items[tab.id];
34171         tab.fireEvent("close", tab);
34172         tab.purgeListeners();
34173         this.autoSizeTabs();
34174     },
34175
34176     getNextAvailable : function(start){
34177         var items = this.items;
34178         var index = start;
34179         // look for a next tab that will slide over to
34180         // replace the one being removed
34181         while(index < items.length){
34182             var item = items[++index];
34183             if(item && !item.isHidden()){
34184                 return item;
34185             }
34186         }
34187         // if one isn't found select the previous tab (on the left)
34188         index = start;
34189         while(index >= 0){
34190             var item = items[--index];
34191             if(item && !item.isHidden()){
34192                 return item;
34193             }
34194         }
34195         return null;
34196     },
34197
34198     /**
34199      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34200      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34201      */
34202     disableTab : function(id){
34203         var tab = this.items[id];
34204         if(tab && this.active != tab){
34205             tab.disable();
34206         }
34207     },
34208
34209     /**
34210      * Enables a {@link Roo.TabPanelItem} that is disabled.
34211      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34212      */
34213     enableTab : function(id){
34214         var tab = this.items[id];
34215         tab.enable();
34216     },
34217
34218     /**
34219      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34220      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34221      * @return {Roo.TabPanelItem} The TabPanelItem.
34222      */
34223     activate : function(id){
34224         var tab = this.items[id];
34225         if(!tab){
34226             return null;
34227         }
34228         if(tab == this.active || tab.disabled){
34229             return tab;
34230         }
34231         var e = {};
34232         this.fireEvent("beforetabchange", this, e, tab);
34233         if(e.cancel !== true && !tab.disabled){
34234             if(this.active){
34235                 this.active.hide();
34236             }
34237             this.active = this.items[id];
34238             this.active.show();
34239             this.fireEvent("tabchange", this, this.active);
34240         }
34241         return tab;
34242     },
34243
34244     /**
34245      * Gets the active {@link Roo.TabPanelItem}.
34246      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34247      */
34248     getActiveTab : function(){
34249         return this.active;
34250     },
34251
34252     /**
34253      * Updates the tab body element to fit the height of the container element
34254      * for overflow scrolling
34255      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34256      */
34257     syncHeight : function(targetHeight){
34258         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34259         var bm = this.bodyEl.getMargins();
34260         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34261         this.bodyEl.setHeight(newHeight);
34262         return newHeight;
34263     },
34264
34265     onResize : function(){
34266         if(this.monitorResize){
34267             this.autoSizeTabs();
34268         }
34269     },
34270
34271     /**
34272      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34273      */
34274     beginUpdate : function(){
34275         this.updating = true;
34276     },
34277
34278     /**
34279      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34280      */
34281     endUpdate : function(){
34282         this.updating = false;
34283         this.autoSizeTabs();
34284     },
34285
34286     /**
34287      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34288      */
34289     autoSizeTabs : function(){
34290         var count = this.items.length;
34291         var vcount = count - this.hiddenCount;
34292         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34293             return;
34294         }
34295         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34296         var availWidth = Math.floor(w / vcount);
34297         var b = this.stripBody;
34298         if(b.getWidth() > w){
34299             var tabs = this.items;
34300             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34301             if(availWidth < this.minTabWidth){
34302                 /*if(!this.sleft){    // incomplete scrolling code
34303                     this.createScrollButtons();
34304                 }
34305                 this.showScroll();
34306                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34307             }
34308         }else{
34309             if(this.currentTabWidth < this.preferredTabWidth){
34310                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34311             }
34312         }
34313     },
34314
34315     /**
34316      * Returns the number of tabs in this TabPanel.
34317      * @return {Number}
34318      */
34319      getCount : function(){
34320          return this.items.length;
34321      },
34322
34323     /**
34324      * Resizes all the tabs to the passed width
34325      * @param {Number} The new width
34326      */
34327     setTabWidth : function(width){
34328         this.currentTabWidth = width;
34329         for(var i = 0, len = this.items.length; i < len; i++) {
34330                 if(!this.items[i].isHidden()) {
34331                 this.items[i].setWidth(width);
34332             }
34333         }
34334     },
34335
34336     /**
34337      * Destroys this TabPanel
34338      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34339      */
34340     destroy : function(removeEl){
34341         Roo.EventManager.removeResizeListener(this.onResize, this);
34342         for(var i = 0, len = this.items.length; i < len; i++){
34343             this.items[i].purgeListeners();
34344         }
34345         if(removeEl === true){
34346             this.el.update("");
34347             this.el.remove();
34348         }
34349     },
34350     
34351     createStrip : function(container)
34352     {
34353         var strip = document.createElement("nav");
34354         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34355         container.appendChild(strip);
34356         return strip;
34357     },
34358     
34359     createStripList : function(strip)
34360     {
34361         // div wrapper for retard IE
34362         // returns the "tr" element.
34363         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34364         //'<div class="x-tabs-strip-wrap">'+
34365           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34366           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34367         return strip.firstChild; //.firstChild.firstChild.firstChild;
34368     },
34369     createBody : function(container)
34370     {
34371         var body = document.createElement("div");
34372         Roo.id(body, "tab-body");
34373         //Roo.fly(body).addClass("x-tabs-body");
34374         Roo.fly(body).addClass("tab-content");
34375         container.appendChild(body);
34376         return body;
34377     },
34378     createItemBody :function(bodyEl, id){
34379         var body = Roo.getDom(id);
34380         if(!body){
34381             body = document.createElement("div");
34382             body.id = id;
34383         }
34384         //Roo.fly(body).addClass("x-tabs-item-body");
34385         Roo.fly(body).addClass("tab-pane");
34386          bodyEl.insertBefore(body, bodyEl.firstChild);
34387         return body;
34388     },
34389     /** @private */
34390     createStripElements :  function(stripEl, text, closable)
34391     {
34392         var td = document.createElement("li"); // was td..
34393         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34394         //stripEl.appendChild(td);
34395         /*if(closable){
34396             td.className = "x-tabs-closable";
34397             if(!this.closeTpl){
34398                 this.closeTpl = new Roo.Template(
34399                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34400                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34401                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34402                 );
34403             }
34404             var el = this.closeTpl.overwrite(td, {"text": text});
34405             var close = el.getElementsByTagName("div")[0];
34406             var inner = el.getElementsByTagName("em")[0];
34407             return {"el": el, "close": close, "inner": inner};
34408         } else {
34409         */
34410         // not sure what this is..
34411             if(!this.tabTpl){
34412                 //this.tabTpl = new Roo.Template(
34413                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34414                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34415                 //);
34416                 this.tabTpl = new Roo.Template(
34417                    '<a href="#">' +
34418                    '<span unselectable="on"' +
34419                             (this.disableTooltips ? '' : ' title="{text}"') +
34420                             ' >{text}</span></span></a>'
34421                 );
34422                 
34423             }
34424             var el = this.tabTpl.overwrite(td, {"text": text});
34425             var inner = el.getElementsByTagName("span")[0];
34426             return {"el": el, "inner": inner};
34427         //}
34428     }
34429         
34430     
34431 });
34432
34433 /**
34434  * @class Roo.TabPanelItem
34435  * @extends Roo.util.Observable
34436  * Represents an individual item (tab plus body) in a TabPanel.
34437  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34438  * @param {String} id The id of this TabPanelItem
34439  * @param {String} text The text for the tab of this TabPanelItem
34440  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34441  */
34442 Roo.bootstrap.panel.TabItem = function(config){
34443     /**
34444      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34445      * @type Roo.TabPanel
34446      */
34447     this.tabPanel = config.panel;
34448     /**
34449      * The id for this TabPanelItem
34450      * @type String
34451      */
34452     this.id = config.id;
34453     /** @private */
34454     this.disabled = false;
34455     /** @private */
34456     this.text = config.text;
34457     /** @private */
34458     this.loaded = false;
34459     this.closable = config.closable;
34460
34461     /**
34462      * The body element for this TabPanelItem.
34463      * @type Roo.Element
34464      */
34465     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34466     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34467     this.bodyEl.setStyle("display", "block");
34468     this.bodyEl.setStyle("zoom", "1");
34469     //this.hideAction();
34470
34471     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34472     /** @private */
34473     this.el = Roo.get(els.el);
34474     this.inner = Roo.get(els.inner, true);
34475     this.textEl = Roo.get(this.el.dom.firstChild, true);
34476     this.pnode = Roo.get(els.el.parentNode, true);
34477     this.el.on("mousedown", this.onTabMouseDown, this);
34478     this.el.on("click", this.onTabClick, this);
34479     /** @private */
34480     if(config.closable){
34481         var c = Roo.get(els.close, true);
34482         c.dom.title = this.closeText;
34483         c.addClassOnOver("close-over");
34484         c.on("click", this.closeClick, this);
34485      }
34486
34487     this.addEvents({
34488          /**
34489          * @event activate
34490          * Fires when this tab becomes the active tab.
34491          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34492          * @param {Roo.TabPanelItem} this
34493          */
34494         "activate": true,
34495         /**
34496          * @event beforeclose
34497          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34498          * @param {Roo.TabPanelItem} this
34499          * @param {Object} e Set cancel to true on this object to cancel the close.
34500          */
34501         "beforeclose": true,
34502         /**
34503          * @event close
34504          * Fires when this tab is closed.
34505          * @param {Roo.TabPanelItem} this
34506          */
34507          "close": true,
34508         /**
34509          * @event deactivate
34510          * Fires when this tab is no longer the active tab.
34511          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34512          * @param {Roo.TabPanelItem} this
34513          */
34514          "deactivate" : true
34515     });
34516     this.hidden = false;
34517
34518     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34519 };
34520
34521 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34522            {
34523     purgeListeners : function(){
34524        Roo.util.Observable.prototype.purgeListeners.call(this);
34525        this.el.removeAllListeners();
34526     },
34527     /**
34528      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34529      */
34530     show : function(){
34531         this.pnode.addClass("active");
34532         this.showAction();
34533         if(Roo.isOpera){
34534             this.tabPanel.stripWrap.repaint();
34535         }
34536         this.fireEvent("activate", this.tabPanel, this);
34537     },
34538
34539     /**
34540      * Returns true if this tab is the active tab.
34541      * @return {Boolean}
34542      */
34543     isActive : function(){
34544         return this.tabPanel.getActiveTab() == this;
34545     },
34546
34547     /**
34548      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34549      */
34550     hide : function(){
34551         this.pnode.removeClass("active");
34552         this.hideAction();
34553         this.fireEvent("deactivate", this.tabPanel, this);
34554     },
34555
34556     hideAction : function(){
34557         this.bodyEl.hide();
34558         this.bodyEl.setStyle("position", "absolute");
34559         this.bodyEl.setLeft("-20000px");
34560         this.bodyEl.setTop("-20000px");
34561     },
34562
34563     showAction : function(){
34564         this.bodyEl.setStyle("position", "relative");
34565         this.bodyEl.setTop("");
34566         this.bodyEl.setLeft("");
34567         this.bodyEl.show();
34568     },
34569
34570     /**
34571      * Set the tooltip for the tab.
34572      * @param {String} tooltip The tab's tooltip
34573      */
34574     setTooltip : function(text){
34575         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34576             this.textEl.dom.qtip = text;
34577             this.textEl.dom.removeAttribute('title');
34578         }else{
34579             this.textEl.dom.title = text;
34580         }
34581     },
34582
34583     onTabClick : function(e){
34584         e.preventDefault();
34585         this.tabPanel.activate(this.id);
34586     },
34587
34588     onTabMouseDown : function(e){
34589         e.preventDefault();
34590         this.tabPanel.activate(this.id);
34591     },
34592 /*
34593     getWidth : function(){
34594         return this.inner.getWidth();
34595     },
34596
34597     setWidth : function(width){
34598         var iwidth = width - this.pnode.getPadding("lr");
34599         this.inner.setWidth(iwidth);
34600         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34601         this.pnode.setWidth(width);
34602     },
34603 */
34604     /**
34605      * Show or hide the tab
34606      * @param {Boolean} hidden True to hide or false to show.
34607      */
34608     setHidden : function(hidden){
34609         this.hidden = hidden;
34610         this.pnode.setStyle("display", hidden ? "none" : "");
34611     },
34612
34613     /**
34614      * Returns true if this tab is "hidden"
34615      * @return {Boolean}
34616      */
34617     isHidden : function(){
34618         return this.hidden;
34619     },
34620
34621     /**
34622      * Returns the text for this tab
34623      * @return {String}
34624      */
34625     getText : function(){
34626         return this.text;
34627     },
34628     /*
34629     autoSize : function(){
34630         //this.el.beginMeasure();
34631         this.textEl.setWidth(1);
34632         /*
34633          *  #2804 [new] Tabs in Roojs
34634          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34635          */
34636         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34637         //this.el.endMeasure();
34638     //},
34639
34640     /**
34641      * Sets the text for the tab (Note: this also sets the tooltip text)
34642      * @param {String} text The tab's text and tooltip
34643      */
34644     setText : function(text){
34645         this.text = text;
34646         this.textEl.update(text);
34647         this.setTooltip(text);
34648         //if(!this.tabPanel.resizeTabs){
34649         //    this.autoSize();
34650         //}
34651     },
34652     /**
34653      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34654      */
34655     activate : function(){
34656         this.tabPanel.activate(this.id);
34657     },
34658
34659     /**
34660      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34661      */
34662     disable : function(){
34663         if(this.tabPanel.active != this){
34664             this.disabled = true;
34665             this.pnode.addClass("disabled");
34666         }
34667     },
34668
34669     /**
34670      * Enables this TabPanelItem if it was previously disabled.
34671      */
34672     enable : function(){
34673         this.disabled = false;
34674         this.pnode.removeClass("disabled");
34675     },
34676
34677     /**
34678      * Sets the content for this TabPanelItem.
34679      * @param {String} content The content
34680      * @param {Boolean} loadScripts true to look for and load scripts
34681      */
34682     setContent : function(content, loadScripts){
34683         this.bodyEl.update(content, loadScripts);
34684     },
34685
34686     /**
34687      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34688      * @return {Roo.UpdateManager} The UpdateManager
34689      */
34690     getUpdateManager : function(){
34691         return this.bodyEl.getUpdateManager();
34692     },
34693
34694     /**
34695      * Set a URL to be used to load the content for this TabPanelItem.
34696      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34697      * @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)
34698      * @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)
34699      * @return {Roo.UpdateManager} The UpdateManager
34700      */
34701     setUrl : function(url, params, loadOnce){
34702         if(this.refreshDelegate){
34703             this.un('activate', this.refreshDelegate);
34704         }
34705         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34706         this.on("activate", this.refreshDelegate);
34707         return this.bodyEl.getUpdateManager();
34708     },
34709
34710     /** @private */
34711     _handleRefresh : function(url, params, loadOnce){
34712         if(!loadOnce || !this.loaded){
34713             var updater = this.bodyEl.getUpdateManager();
34714             updater.update(url, params, this._setLoaded.createDelegate(this));
34715         }
34716     },
34717
34718     /**
34719      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34720      *   Will fail silently if the setUrl method has not been called.
34721      *   This does not activate the panel, just updates its content.
34722      */
34723     refresh : function(){
34724         if(this.refreshDelegate){
34725            this.loaded = false;
34726            this.refreshDelegate();
34727         }
34728     },
34729
34730     /** @private */
34731     _setLoaded : function(){
34732         this.loaded = true;
34733     },
34734
34735     /** @private */
34736     closeClick : function(e){
34737         var o = {};
34738         e.stopEvent();
34739         this.fireEvent("beforeclose", this, o);
34740         if(o.cancel !== true){
34741             this.tabPanel.removeTab(this.id);
34742         }
34743     },
34744     /**
34745      * The text displayed in the tooltip for the close icon.
34746      * @type String
34747      */
34748     closeText : "Close this tab"
34749 });