roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     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             }
6008             
6009             var cellIndex = cell.dom.cellIndex;
6010             
6011             var ename = name == 'touchstart' ? 'click' : name;
6012             this.fireEvent("header" + ename, this, cellIndex, e);
6013             
6014             return;
6015         }
6016         
6017         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6018             cell = Roo.get(t).findParent('td', false, true);
6019         }
6020         
6021         var row = cell.findParent('tr', false, true);
6022         var cellIndex = cell.dom.cellIndex;
6023         var rowIndex = row.dom.rowIndex - 1;
6024         
6025         if(row !== false){
6026             
6027             this.fireEvent("row" + name, this, rowIndex, e);
6028             
6029             if(cell !== false){
6030             
6031                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6032             }
6033         }
6034         
6035     },
6036     
6037     onMouseover : function(e, el)
6038     {
6039         var cell = Roo.get(el);
6040         
6041         if(!cell){
6042             return;
6043         }
6044         
6045         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6046             cell = cell.findParent('td', false, true);
6047         }
6048         
6049         var row = cell.findParent('tr', false, true);
6050         var cellIndex = cell.dom.cellIndex;
6051         var rowIndex = row.dom.rowIndex - 1; // start from 0
6052         
6053         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6054         
6055     },
6056     
6057     onMouseout : function(e, el)
6058     {
6059         var cell = Roo.get(el);
6060         
6061         if(!cell){
6062             return;
6063         }
6064         
6065         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6066             cell = cell.findParent('td', false, true);
6067         }
6068         
6069         var row = cell.findParent('tr', false, true);
6070         var cellIndex = cell.dom.cellIndex;
6071         var rowIndex = row.dom.rowIndex - 1; // start from 0
6072         
6073         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6074         
6075     },
6076     
6077     onClick : function(e, el)
6078     {
6079         var cell = Roo.get(el);
6080         
6081         if(!cell || (!this.cellSelection && !this.rowSelection)){
6082             return;
6083         }
6084         
6085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086             cell = cell.findParent('td', false, true);
6087         }
6088         
6089         if(!cell || typeof(cell) == 'undefined'){
6090             return;
6091         }
6092         
6093         var row = cell.findParent('tr', false, true);
6094         
6095         if(!row || typeof(row) == 'undefined'){
6096             return;
6097         }
6098         
6099         var cellIndex = cell.dom.cellIndex;
6100         var rowIndex = this.getRowIndex(row);
6101         
6102         // why??? - should these not be based on SelectionModel?
6103         if(this.cellSelection){
6104             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6105         }
6106         
6107         if(this.rowSelection){
6108             this.fireEvent('rowclick', this, row, rowIndex, e);
6109         }
6110         
6111         
6112     },
6113     
6114     onDblClick : function(e,el)
6115     {
6116         var cell = Roo.get(el);
6117         
6118         if(!cell || (!this.CellSelection && !this.RowSelection)){
6119             return;
6120         }
6121         
6122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6123             cell = cell.findParent('td', false, true);
6124         }
6125         
6126         if(!cell || typeof(cell) == 'undefined'){
6127             return;
6128         }
6129         
6130         var row = cell.findParent('tr', false, true);
6131         
6132         if(!row || typeof(row) == 'undefined'){
6133             return;
6134         }
6135         
6136         var cellIndex = cell.dom.cellIndex;
6137         var rowIndex = this.getRowIndex(row);
6138         
6139         if(this.CellSelection){
6140             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6141         }
6142         
6143         if(this.RowSelection){
6144             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6145         }
6146     },
6147     
6148     sort : function(e,el)
6149     {
6150         var col = Roo.get(el);
6151         
6152         if(!col.hasClass('sortable')){
6153             return;
6154         }
6155         
6156         var sort = col.attr('sort');
6157         var dir = 'ASC';
6158         
6159         if(col.hasClass('glyphicon-arrow-up')){
6160             dir = 'DESC';
6161         }
6162         
6163         this.store.sortInfo = {field : sort, direction : dir};
6164         
6165         if (this.footer) {
6166             Roo.log("calling footer first");
6167             this.footer.onClick('first');
6168         } else {
6169         
6170             this.store.load({ params : { start : 0 } });
6171         }
6172     },
6173     
6174     renderHeader : function()
6175     {
6176         var header = {
6177             tag: 'thead',
6178             cn : []
6179         };
6180         
6181         var cm = this.cm;
6182         
6183         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6184             
6185             var config = cm.config[i];
6186             
6187             var c = {
6188                 tag: 'th',
6189                 style : '',
6190                 html: cm.getColumnHeader(i)
6191             };
6192             
6193             var hh = '';
6194             
6195             if(typeof(config.lgHeader) != 'undefined'){
6196                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6197             }
6198             
6199             if(typeof(config.mdHeader) != 'undefined'){
6200                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6201             }
6202             
6203             if(typeof(config.smHeader) != 'undefined'){
6204                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6205             }
6206             
6207             if(typeof(config.xsHeader) != 'undefined'){
6208                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6209             }
6210             
6211             if(hh.length){
6212                 c.html = hh;
6213             }
6214             
6215             if(typeof(config.tooltip) != 'undefined'){
6216                 c.tooltip = config.tooltip;
6217             }
6218             
6219             if(typeof(config.colspan) != 'undefined'){
6220                 c.colspan = config.colspan;
6221             }
6222             
6223             if(typeof(config.hidden) != 'undefined' && config.hidden){
6224                 c.style += ' display:none;';
6225             }
6226             
6227             if(typeof(config.dataIndex) != 'undefined'){
6228                 c.sort = config.dataIndex;
6229             }
6230             
6231             if(typeof(config.sortable) != 'undefined' && config.sortable){
6232                 c.cls = 'sortable';
6233             }
6234             
6235             if(typeof(config.align) != 'undefined' && config.align.length){
6236                 c.style += ' text-align:' + config.align + ';';
6237             }
6238             
6239             if(typeof(config.width) != 'undefined'){
6240                 c.style += ' width:' + config.width + 'px;';
6241             }
6242             
6243             if(typeof(config.cls) != 'undefined'){
6244                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6245             }
6246             
6247             ['xs','sm','md','lg'].map(function(size){
6248                 
6249                 if(typeof(config[size]) == 'undefined'){
6250                     return;
6251                 }
6252                 
6253                 if (!config[size]) { // 0 = hidden
6254                     c.cls += ' hidden-' + size;
6255                     return;
6256                 }
6257                 
6258                 c.cls += ' col-' + size + '-' + config[size];
6259
6260             });
6261             
6262             header.cn.push(c)
6263         }
6264         
6265         return header;
6266     },
6267     
6268     renderBody : function()
6269     {
6270         var body = {
6271             tag: 'tbody',
6272             cn : [
6273                 {
6274                     tag: 'tr',
6275                     cn : [
6276                         {
6277                             tag : 'td',
6278                             colspan :  this.cm.getColumnCount()
6279                         }
6280                     ]
6281                 }
6282             ]
6283         };
6284         
6285         return body;
6286     },
6287     
6288     renderFooter : function()
6289     {
6290         var footer = {
6291             tag: 'tfoot',
6292             cn : [
6293                 {
6294                     tag: 'tr',
6295                     cn : [
6296                         {
6297                             tag : 'td',
6298                             colspan :  this.cm.getColumnCount()
6299                         }
6300                     ]
6301                 }
6302             ]
6303         };
6304         
6305         return footer;
6306     },
6307     
6308     
6309     
6310     onLoad : function()
6311     {
6312 //        Roo.log('ds onload');
6313         this.clear();
6314         
6315         var _this = this;
6316         var cm = this.cm;
6317         var ds = this.store;
6318         
6319         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6320             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6321             if (_this.store.sortInfo) {
6322                     
6323                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6324                     e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6325                 }
6326                 
6327                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6328                     e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6329                 }
6330             }
6331         });
6332         
6333         var tbody =  this.mainBody;
6334               
6335         if(ds.getCount() > 0){
6336             ds.data.each(function(d,rowIndex){
6337                 var row =  this.renderRow(cm, ds, rowIndex);
6338                 
6339                 tbody.createChild(row);
6340                 
6341                 var _this = this;
6342                 
6343                 if(row.cellObjects.length){
6344                     Roo.each(row.cellObjects, function(r){
6345                         _this.renderCellObject(r);
6346                     })
6347                 }
6348                 
6349             }, this);
6350         }
6351         
6352         Roo.each(this.el.select('tbody td', true).elements, function(e){
6353             e.on('mouseover', _this.onMouseover, _this);
6354         });
6355         
6356         Roo.each(this.el.select('tbody td', true).elements, function(e){
6357             e.on('mouseout', _this.onMouseout, _this);
6358         });
6359         this.fireEvent('rowsrendered', this);
6360         //if(this.loadMask){
6361         //    this.maskEl.hide();
6362         //}
6363     },
6364     
6365     
6366     onUpdate : function(ds,record)
6367     {
6368         this.refreshRow(record);
6369     },
6370     
6371     onRemove : function(ds, record, index, isUpdate){
6372         if(isUpdate !== true){
6373             this.fireEvent("beforerowremoved", this, index, record);
6374         }
6375         var bt = this.mainBody.dom;
6376         
6377         var rows = this.el.select('tbody > tr', true).elements;
6378         
6379         if(typeof(rows[index]) != 'undefined'){
6380             bt.removeChild(rows[index].dom);
6381         }
6382         
6383 //        if(bt.rows[index]){
6384 //            bt.removeChild(bt.rows[index]);
6385 //        }
6386         
6387         if(isUpdate !== true){
6388             //this.stripeRows(index);
6389             //this.syncRowHeights(index, index);
6390             //this.layout();
6391             this.fireEvent("rowremoved", this, index, record);
6392         }
6393     },
6394     
6395     onAdd : function(ds, records, rowIndex)
6396     {
6397         //Roo.log('on Add called');
6398         // - note this does not handle multiple adding very well..
6399         var bt = this.mainBody.dom;
6400         for (var i =0 ; i < records.length;i++) {
6401             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6402             //Roo.log(records[i]);
6403             //Roo.log(this.store.getAt(rowIndex+i));
6404             this.insertRow(this.store, rowIndex + i, false);
6405             return;
6406         }
6407         
6408     },
6409     
6410     
6411     refreshRow : function(record){
6412         var ds = this.store, index;
6413         if(typeof record == 'number'){
6414             index = record;
6415             record = ds.getAt(index);
6416         }else{
6417             index = ds.indexOf(record);
6418         }
6419         this.insertRow(ds, index, true);
6420         this.onRemove(ds, record, index+1, true);
6421         //this.syncRowHeights(index, index);
6422         //this.layout();
6423         this.fireEvent("rowupdated", this, index, record);
6424     },
6425     
6426     insertRow : function(dm, rowIndex, isUpdate){
6427         
6428         if(!isUpdate){
6429             this.fireEvent("beforerowsinserted", this, rowIndex);
6430         }
6431             //var s = this.getScrollState();
6432         var row = this.renderRow(this.cm, this.store, rowIndex);
6433         // insert before rowIndex..
6434         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6435         
6436         var _this = this;
6437                 
6438         if(row.cellObjects.length){
6439             Roo.each(row.cellObjects, function(r){
6440                 _this.renderCellObject(r);
6441             })
6442         }
6443             
6444         if(!isUpdate){
6445             this.fireEvent("rowsinserted", this, rowIndex);
6446             //this.syncRowHeights(firstRow, lastRow);
6447             //this.stripeRows(firstRow);
6448             //this.layout();
6449         }
6450         
6451     },
6452     
6453     
6454     getRowDom : function(rowIndex)
6455     {
6456         var rows = this.el.select('tbody > tr', true).elements;
6457         
6458         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6459         
6460     },
6461     // returns the object tree for a tr..
6462   
6463     
6464     renderRow : function(cm, ds, rowIndex) 
6465     {
6466         
6467         var d = ds.getAt(rowIndex);
6468         
6469         var row = {
6470             tag : 'tr',
6471             cn : []
6472         };
6473             
6474         var cellObjects = [];
6475         
6476         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6477             var config = cm.config[i];
6478             
6479             var renderer = cm.getRenderer(i);
6480             var value = '';
6481             var id = false;
6482             
6483             if(typeof(renderer) !== 'undefined'){
6484                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6485             }
6486             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6487             // and are rendered into the cells after the row is rendered - using the id for the element.
6488             
6489             if(typeof(value) === 'object'){
6490                 id = Roo.id();
6491                 cellObjects.push({
6492                     container : id,
6493                     cfg : value 
6494                 })
6495             }
6496             
6497             var rowcfg = {
6498                 record: d,
6499                 rowIndex : rowIndex,
6500                 colIndex : i,
6501                 rowClass : ''
6502             };
6503
6504             this.fireEvent('rowclass', this, rowcfg);
6505             
6506             var td = {
6507                 tag: 'td',
6508                 cls : rowcfg.rowClass,
6509                 style: '',
6510                 html: (typeof(value) === 'object') ? '' : value
6511             };
6512             
6513             if (id) {
6514                 td.id = id;
6515             }
6516             
6517             if(typeof(config.colspan) != 'undefined'){
6518                 td.colspan = config.colspan;
6519             }
6520             
6521             if(typeof(config.hidden) != 'undefined' && config.hidden){
6522                 td.style += ' display:none;';
6523             }
6524             
6525             if(typeof(config.align) != 'undefined' && config.align.length){
6526                 td.style += ' text-align:' + config.align + ';';
6527             }
6528             
6529             if(typeof(config.width) != 'undefined'){
6530                 td.style += ' width:' +  config.width + 'px;';
6531             }
6532             
6533             if(typeof(config.cursor) != 'undefined'){
6534                 td.style += ' cursor:' +  config.cursor + ';';
6535             }
6536             
6537             if(typeof(config.cls) != 'undefined'){
6538                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6539             }
6540             
6541             ['xs','sm','md','lg'].map(function(size){
6542                 
6543                 if(typeof(config[size]) == 'undefined'){
6544                     return;
6545                 }
6546                 
6547                 if (!config[size]) { // 0 = hidden
6548                     td.cls += ' hidden-' + size;
6549                     return;
6550                 }
6551                 
6552                 td.cls += ' col-' + size + '-' + config[size];
6553
6554             });
6555              
6556             row.cn.push(td);
6557            
6558         }
6559         
6560         row.cellObjects = cellObjects;
6561         
6562         return row;
6563           
6564     },
6565     
6566     
6567     
6568     onBeforeLoad : function()
6569     {
6570         //Roo.log('ds onBeforeLoad');
6571         
6572         //this.clear();
6573         
6574         //if(this.loadMask){
6575         //    this.maskEl.show();
6576         //}
6577     },
6578      /**
6579      * Remove all rows
6580      */
6581     clear : function()
6582     {
6583         this.el.select('tbody', true).first().dom.innerHTML = '';
6584     },
6585     /**
6586      * Show or hide a row.
6587      * @param {Number} rowIndex to show or hide
6588      * @param {Boolean} state hide
6589      */
6590     setRowVisibility : function(rowIndex, state)
6591     {
6592         var bt = this.mainBody.dom;
6593         
6594         var rows = this.el.select('tbody > tr', true).elements;
6595         
6596         if(typeof(rows[rowIndex]) == 'undefined'){
6597             return;
6598         }
6599         rows[rowIndex].dom.style.display = state ? '' : 'none';
6600     },
6601     
6602     
6603     getSelectionModel : function(){
6604         if(!this.selModel){
6605             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6606         }
6607         return this.selModel;
6608     },
6609     /*
6610      * Render the Roo.bootstrap object from renderder
6611      */
6612     renderCellObject : function(r)
6613     {
6614         var _this = this;
6615         
6616         var t = r.cfg.render(r.container);
6617         
6618         if(r.cfg.cn){
6619             Roo.each(r.cfg.cn, function(c){
6620                 var child = {
6621                     container: t.getChildContainer(),
6622                     cfg: c
6623                 };
6624                 _this.renderCellObject(child);
6625             })
6626         }
6627     },
6628     
6629     getRowIndex : function(row)
6630     {
6631         var rowIndex = -1;
6632         
6633         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6634             if(el != row){
6635                 return;
6636             }
6637             
6638             rowIndex = index;
6639         });
6640         
6641         return rowIndex;
6642     },
6643      /**
6644      * Returns the grid's underlying element = used by panel.Grid
6645      * @return {Element} The element
6646      */
6647     getGridEl : function(){
6648         return this.container;
6649     },
6650      /**
6651      * Forces a resize - used by panel.Grid
6652      * @return {Element} The element
6653      */
6654     autoSize : function(){
6655         return; // we doe not have a view in this design..
6656         if(this.rendered){
6657             this.view.layout();
6658             if(this.view.adjustForScroll){
6659                 this.view.adjustForScroll();
6660             }
6661         }
6662     }
6663 });
6664
6665  
6666
6667  /*
6668  * - LGPL
6669  *
6670  * table cell
6671  * 
6672  */
6673
6674 /**
6675  * @class Roo.bootstrap.TableCell
6676  * @extends Roo.bootstrap.Component
6677  * Bootstrap TableCell class
6678  * @cfg {String} html cell contain text
6679  * @cfg {String} cls cell class
6680  * @cfg {String} tag cell tag (td|th) default td
6681  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6682  * @cfg {String} align Aligns the content in a cell
6683  * @cfg {String} axis Categorizes cells
6684  * @cfg {String} bgcolor Specifies the background color of a cell
6685  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6686  * @cfg {Number} colspan Specifies the number of columns a cell should span
6687  * @cfg {String} headers Specifies one or more header cells a cell is related to
6688  * @cfg {Number} height Sets the height of a cell
6689  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6690  * @cfg {Number} rowspan Sets the number of rows a cell should span
6691  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6692  * @cfg {String} valign Vertical aligns the content in a cell
6693  * @cfg {Number} width Specifies the width of a cell
6694  * 
6695  * @constructor
6696  * Create a new TableCell
6697  * @param {Object} config The config object
6698  */
6699
6700 Roo.bootstrap.TableCell = function(config){
6701     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6702 };
6703
6704 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6705     
6706     html: false,
6707     cls: false,
6708     tag: false,
6709     abbr: false,
6710     align: false,
6711     axis: false,
6712     bgcolor: false,
6713     charoff: false,
6714     colspan: false,
6715     headers: false,
6716     height: false,
6717     nowrap: false,
6718     rowspan: false,
6719     scope: false,
6720     valign: false,
6721     width: false,
6722     
6723     
6724     getAutoCreate : function(){
6725         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6726         
6727         cfg = {
6728             tag: 'td'
6729         };
6730         
6731         if(this.tag){
6732             cfg.tag = this.tag;
6733         }
6734         
6735         if (this.html) {
6736             cfg.html=this.html
6737         }
6738         if (this.cls) {
6739             cfg.cls=this.cls
6740         }
6741         if (this.abbr) {
6742             cfg.abbr=this.abbr
6743         }
6744         if (this.align) {
6745             cfg.align=this.align
6746         }
6747         if (this.axis) {
6748             cfg.axis=this.axis
6749         }
6750         if (this.bgcolor) {
6751             cfg.bgcolor=this.bgcolor
6752         }
6753         if (this.charoff) {
6754             cfg.charoff=this.charoff
6755         }
6756         if (this.colspan) {
6757             cfg.colspan=this.colspan
6758         }
6759         if (this.headers) {
6760             cfg.headers=this.headers
6761         }
6762         if (this.height) {
6763             cfg.height=this.height
6764         }
6765         if (this.nowrap) {
6766             cfg.nowrap=this.nowrap
6767         }
6768         if (this.rowspan) {
6769             cfg.rowspan=this.rowspan
6770         }
6771         if (this.scope) {
6772             cfg.scope=this.scope
6773         }
6774         if (this.valign) {
6775             cfg.valign=this.valign
6776         }
6777         if (this.width) {
6778             cfg.width=this.width
6779         }
6780         
6781         
6782         return cfg;
6783     }
6784    
6785 });
6786
6787  
6788
6789  /*
6790  * - LGPL
6791  *
6792  * table row
6793  * 
6794  */
6795
6796 /**
6797  * @class Roo.bootstrap.TableRow
6798  * @extends Roo.bootstrap.Component
6799  * Bootstrap TableRow class
6800  * @cfg {String} cls row class
6801  * @cfg {String} align Aligns the content in a table row
6802  * @cfg {String} bgcolor Specifies a background color for a table row
6803  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6804  * @cfg {String} valign Vertical aligns the content in a table row
6805  * 
6806  * @constructor
6807  * Create a new TableRow
6808  * @param {Object} config The config object
6809  */
6810
6811 Roo.bootstrap.TableRow = function(config){
6812     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6813 };
6814
6815 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6816     
6817     cls: false,
6818     align: false,
6819     bgcolor: false,
6820     charoff: false,
6821     valign: false,
6822     
6823     getAutoCreate : function(){
6824         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6825         
6826         cfg = {
6827             tag: 'tr'
6828         };
6829             
6830         if(this.cls){
6831             cfg.cls = this.cls;
6832         }
6833         if(this.align){
6834             cfg.align = this.align;
6835         }
6836         if(this.bgcolor){
6837             cfg.bgcolor = this.bgcolor;
6838         }
6839         if(this.charoff){
6840             cfg.charoff = this.charoff;
6841         }
6842         if(this.valign){
6843             cfg.valign = this.valign;
6844         }
6845         
6846         return cfg;
6847     }
6848    
6849 });
6850
6851  
6852
6853  /*
6854  * - LGPL
6855  *
6856  * table body
6857  * 
6858  */
6859
6860 /**
6861  * @class Roo.bootstrap.TableBody
6862  * @extends Roo.bootstrap.Component
6863  * Bootstrap TableBody class
6864  * @cfg {String} cls element class
6865  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6866  * @cfg {String} align Aligns the content inside the element
6867  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6868  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6869  * 
6870  * @constructor
6871  * Create a new TableBody
6872  * @param {Object} config The config object
6873  */
6874
6875 Roo.bootstrap.TableBody = function(config){
6876     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6877 };
6878
6879 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6880     
6881     cls: false,
6882     tag: false,
6883     align: false,
6884     charoff: false,
6885     valign: false,
6886     
6887     getAutoCreate : function(){
6888         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6889         
6890         cfg = {
6891             tag: 'tbody'
6892         };
6893             
6894         if (this.cls) {
6895             cfg.cls=this.cls
6896         }
6897         if(this.tag){
6898             cfg.tag = this.tag;
6899         }
6900         
6901         if(this.align){
6902             cfg.align = this.align;
6903         }
6904         if(this.charoff){
6905             cfg.charoff = this.charoff;
6906         }
6907         if(this.valign){
6908             cfg.valign = this.valign;
6909         }
6910         
6911         return cfg;
6912     }
6913     
6914     
6915 //    initEvents : function()
6916 //    {
6917 //        
6918 //        if(!this.store){
6919 //            return;
6920 //        }
6921 //        
6922 //        this.store = Roo.factory(this.store, Roo.data);
6923 //        this.store.on('load', this.onLoad, this);
6924 //        
6925 //        this.store.load();
6926 //        
6927 //    },
6928 //    
6929 //    onLoad: function () 
6930 //    {   
6931 //        this.fireEvent('load', this);
6932 //    }
6933 //    
6934 //   
6935 });
6936
6937  
6938
6939  /*
6940  * Based on:
6941  * Ext JS Library 1.1.1
6942  * Copyright(c) 2006-2007, Ext JS, LLC.
6943  *
6944  * Originally Released Under LGPL - original licence link has changed is not relivant.
6945  *
6946  * Fork - LGPL
6947  * <script type="text/javascript">
6948  */
6949
6950 // as we use this in bootstrap.
6951 Roo.namespace('Roo.form');
6952  /**
6953  * @class Roo.form.Action
6954  * Internal Class used to handle form actions
6955  * @constructor
6956  * @param {Roo.form.BasicForm} el The form element or its id
6957  * @param {Object} config Configuration options
6958  */
6959
6960  
6961  
6962 // define the action interface
6963 Roo.form.Action = function(form, options){
6964     this.form = form;
6965     this.options = options || {};
6966 };
6967 /**
6968  * Client Validation Failed
6969  * @const 
6970  */
6971 Roo.form.Action.CLIENT_INVALID = 'client';
6972 /**
6973  * Server Validation Failed
6974  * @const 
6975  */
6976 Roo.form.Action.SERVER_INVALID = 'server';
6977  /**
6978  * Connect to Server Failed
6979  * @const 
6980  */
6981 Roo.form.Action.CONNECT_FAILURE = 'connect';
6982 /**
6983  * Reading Data from Server Failed
6984  * @const 
6985  */
6986 Roo.form.Action.LOAD_FAILURE = 'load';
6987
6988 Roo.form.Action.prototype = {
6989     type : 'default',
6990     failureType : undefined,
6991     response : undefined,
6992     result : undefined,
6993
6994     // interface method
6995     run : function(options){
6996
6997     },
6998
6999     // interface method
7000     success : function(response){
7001
7002     },
7003
7004     // interface method
7005     handleResponse : function(response){
7006
7007     },
7008
7009     // default connection failure
7010     failure : function(response){
7011         
7012         this.response = response;
7013         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7014         this.form.afterAction(this, false);
7015     },
7016
7017     processResponse : function(response){
7018         this.response = response;
7019         if(!response.responseText){
7020             return true;
7021         }
7022         this.result = this.handleResponse(response);
7023         return this.result;
7024     },
7025
7026     // utility functions used internally
7027     getUrl : function(appendParams){
7028         var url = this.options.url || this.form.url || this.form.el.dom.action;
7029         if(appendParams){
7030             var p = this.getParams();
7031             if(p){
7032                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7033             }
7034         }
7035         return url;
7036     },
7037
7038     getMethod : function(){
7039         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7040     },
7041
7042     getParams : function(){
7043         var bp = this.form.baseParams;
7044         var p = this.options.params;
7045         if(p){
7046             if(typeof p == "object"){
7047                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7048             }else if(typeof p == 'string' && bp){
7049                 p += '&' + Roo.urlEncode(bp);
7050             }
7051         }else if(bp){
7052             p = Roo.urlEncode(bp);
7053         }
7054         return p;
7055     },
7056
7057     createCallback : function(){
7058         return {
7059             success: this.success,
7060             failure: this.failure,
7061             scope: this,
7062             timeout: (this.form.timeout*1000),
7063             upload: this.form.fileUpload ? this.success : undefined
7064         };
7065     }
7066 };
7067
7068 Roo.form.Action.Submit = function(form, options){
7069     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7070 };
7071
7072 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7073     type : 'submit',
7074
7075     haveProgress : false,
7076     uploadComplete : false,
7077     
7078     // uploadProgress indicator.
7079     uploadProgress : function()
7080     {
7081         if (!this.form.progressUrl) {
7082             return;
7083         }
7084         
7085         if (!this.haveProgress) {
7086             Roo.MessageBox.progress("Uploading", "Uploading");
7087         }
7088         if (this.uploadComplete) {
7089            Roo.MessageBox.hide();
7090            return;
7091         }
7092         
7093         this.haveProgress = true;
7094    
7095         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7096         
7097         var c = new Roo.data.Connection();
7098         c.request({
7099             url : this.form.progressUrl,
7100             params: {
7101                 id : uid
7102             },
7103             method: 'GET',
7104             success : function(req){
7105                //console.log(data);
7106                 var rdata = false;
7107                 var edata;
7108                 try  {
7109                    rdata = Roo.decode(req.responseText)
7110                 } catch (e) {
7111                     Roo.log("Invalid data from server..");
7112                     Roo.log(edata);
7113                     return;
7114                 }
7115                 if (!rdata || !rdata.success) {
7116                     Roo.log(rdata);
7117                     Roo.MessageBox.alert(Roo.encode(rdata));
7118                     return;
7119                 }
7120                 var data = rdata.data;
7121                 
7122                 if (this.uploadComplete) {
7123                    Roo.MessageBox.hide();
7124                    return;
7125                 }
7126                    
7127                 if (data){
7128                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7129                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7130                     );
7131                 }
7132                 this.uploadProgress.defer(2000,this);
7133             },
7134        
7135             failure: function(data) {
7136                 Roo.log('progress url failed ');
7137                 Roo.log(data);
7138             },
7139             scope : this
7140         });
7141            
7142     },
7143     
7144     
7145     run : function()
7146     {
7147         // run get Values on the form, so it syncs any secondary forms.
7148         this.form.getValues();
7149         
7150         var o = this.options;
7151         var method = this.getMethod();
7152         var isPost = method == 'POST';
7153         if(o.clientValidation === false || this.form.isValid()){
7154             
7155             if (this.form.progressUrl) {
7156                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7157                     (new Date() * 1) + '' + Math.random());
7158                     
7159             } 
7160             
7161             
7162             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7163                 form:this.form.el.dom,
7164                 url:this.getUrl(!isPost),
7165                 method: method,
7166                 params:isPost ? this.getParams() : null,
7167                 isUpload: this.form.fileUpload
7168             }));
7169             
7170             this.uploadProgress();
7171
7172         }else if (o.clientValidation !== false){ // client validation failed
7173             this.failureType = Roo.form.Action.CLIENT_INVALID;
7174             this.form.afterAction(this, false);
7175         }
7176     },
7177
7178     success : function(response)
7179     {
7180         this.uploadComplete= true;
7181         if (this.haveProgress) {
7182             Roo.MessageBox.hide();
7183         }
7184         
7185         
7186         var result = this.processResponse(response);
7187         if(result === true || result.success){
7188             this.form.afterAction(this, true);
7189             return;
7190         }
7191         if(result.errors){
7192             this.form.markInvalid(result.errors);
7193             this.failureType = Roo.form.Action.SERVER_INVALID;
7194         }
7195         this.form.afterAction(this, false);
7196     },
7197     failure : function(response)
7198     {
7199         this.uploadComplete= true;
7200         if (this.haveProgress) {
7201             Roo.MessageBox.hide();
7202         }
7203         
7204         this.response = response;
7205         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7206         this.form.afterAction(this, false);
7207     },
7208     
7209     handleResponse : function(response){
7210         if(this.form.errorReader){
7211             var rs = this.form.errorReader.read(response);
7212             var errors = [];
7213             if(rs.records){
7214                 for(var i = 0, len = rs.records.length; i < len; i++) {
7215                     var r = rs.records[i];
7216                     errors[i] = r.data;
7217                 }
7218             }
7219             if(errors.length < 1){
7220                 errors = null;
7221             }
7222             return {
7223                 success : rs.success,
7224                 errors : errors
7225             };
7226         }
7227         var ret = false;
7228         try {
7229             ret = Roo.decode(response.responseText);
7230         } catch (e) {
7231             ret = {
7232                 success: false,
7233                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7234                 errors : []
7235             };
7236         }
7237         return ret;
7238         
7239     }
7240 });
7241
7242
7243 Roo.form.Action.Load = function(form, options){
7244     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7245     this.reader = this.form.reader;
7246 };
7247
7248 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7249     type : 'load',
7250
7251     run : function(){
7252         
7253         Roo.Ajax.request(Roo.apply(
7254                 this.createCallback(), {
7255                     method:this.getMethod(),
7256                     url:this.getUrl(false),
7257                     params:this.getParams()
7258         }));
7259     },
7260
7261     success : function(response){
7262         
7263         var result = this.processResponse(response);
7264         if(result === true || !result.success || !result.data){
7265             this.failureType = Roo.form.Action.LOAD_FAILURE;
7266             this.form.afterAction(this, false);
7267             return;
7268         }
7269         this.form.clearInvalid();
7270         this.form.setValues(result.data);
7271         this.form.afterAction(this, true);
7272     },
7273
7274     handleResponse : function(response){
7275         if(this.form.reader){
7276             var rs = this.form.reader.read(response);
7277             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7278             return {
7279                 success : rs.success,
7280                 data : data
7281             };
7282         }
7283         return Roo.decode(response.responseText);
7284     }
7285 });
7286
7287 Roo.form.Action.ACTION_TYPES = {
7288     'load' : Roo.form.Action.Load,
7289     'submit' : Roo.form.Action.Submit
7290 };/*
7291  * - LGPL
7292  *
7293  * form
7294  * 
7295  */
7296
7297 /**
7298  * @class Roo.bootstrap.Form
7299  * @extends Roo.bootstrap.Component
7300  * Bootstrap Form class
7301  * @cfg {String} method  GET | POST (default POST)
7302  * @cfg {String} labelAlign top | left (default top)
7303  * @cfg {String} align left  | right - for navbars
7304  * @cfg {Boolean} loadMask load mask when submit (default true)
7305
7306  * 
7307  * @constructor
7308  * Create a new Form
7309  * @param {Object} config The config object
7310  */
7311
7312
7313 Roo.bootstrap.Form = function(config){
7314     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7315     this.addEvents({
7316         /**
7317          * @event clientvalidation
7318          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7319          * @param {Form} this
7320          * @param {Boolean} valid true if the form has passed client-side validation
7321          */
7322         clientvalidation: true,
7323         /**
7324          * @event beforeaction
7325          * Fires before any action is performed. Return false to cancel the action.
7326          * @param {Form} this
7327          * @param {Action} action The action to be performed
7328          */
7329         beforeaction: true,
7330         /**
7331          * @event actionfailed
7332          * Fires when an action fails.
7333          * @param {Form} this
7334          * @param {Action} action The action that failed
7335          */
7336         actionfailed : true,
7337         /**
7338          * @event actioncomplete
7339          * Fires when an action is completed.
7340          * @param {Form} this
7341          * @param {Action} action The action that completed
7342          */
7343         actioncomplete : true
7344     });
7345     
7346 };
7347
7348 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7349       
7350      /**
7351      * @cfg {String} method
7352      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7353      */
7354     method : 'POST',
7355     /**
7356      * @cfg {String} url
7357      * The URL to use for form actions if one isn't supplied in the action options.
7358      */
7359     /**
7360      * @cfg {Boolean} fileUpload
7361      * Set to true if this form is a file upload.
7362      */
7363      
7364     /**
7365      * @cfg {Object} baseParams
7366      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7367      */
7368       
7369     /**
7370      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7371      */
7372     timeout: 30,
7373     /**
7374      * @cfg {Sting} align (left|right) for navbar forms
7375      */
7376     align : 'left',
7377
7378     // private
7379     activeAction : null,
7380  
7381     /**
7382      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7383      * element by passing it or its id or mask the form itself by passing in true.
7384      * @type Mixed
7385      */
7386     waitMsgTarget : false,
7387     
7388     loadMask : true,
7389     
7390     getAutoCreate : function(){
7391         
7392         var cfg = {
7393             tag: 'form',
7394             method : this.method || 'POST',
7395             id : this.id || Roo.id(),
7396             cls : ''
7397         };
7398         if (this.parent().xtype.match(/^Nav/)) {
7399             cfg.cls = 'navbar-form navbar-' + this.align;
7400             
7401         }
7402         
7403         if (this.labelAlign == 'left' ) {
7404             cfg.cls += ' form-horizontal';
7405         }
7406         
7407         
7408         return cfg;
7409     },
7410     initEvents : function()
7411     {
7412         this.el.on('submit', this.onSubmit, this);
7413         // this was added as random key presses on the form where triggering form submit.
7414         this.el.on('keypress', function(e) {
7415             if (e.getCharCode() != 13) {
7416                 return true;
7417             }
7418             // we might need to allow it for textareas.. and some other items.
7419             // check e.getTarget().
7420             
7421             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7422                 return true;
7423             }
7424         
7425             Roo.log("keypress blocked");
7426             
7427             e.preventDefault();
7428             return false;
7429         });
7430         
7431     },
7432     // private
7433     onSubmit : function(e){
7434         e.stopEvent();
7435     },
7436     
7437      /**
7438      * Returns true if client-side validation on the form is successful.
7439      * @return Boolean
7440      */
7441     isValid : function(){
7442         var items = this.getItems();
7443         var valid = true;
7444         items.each(function(f){
7445            if(!f.validate()){
7446                valid = false;
7447                
7448            }
7449         });
7450         return valid;
7451     },
7452     /**
7453      * Returns true if any fields in this form have changed since their original load.
7454      * @return Boolean
7455      */
7456     isDirty : function(){
7457         var dirty = false;
7458         var items = this.getItems();
7459         items.each(function(f){
7460            if(f.isDirty()){
7461                dirty = true;
7462                return false;
7463            }
7464            return true;
7465         });
7466         return dirty;
7467     },
7468      /**
7469      * Performs a predefined action (submit or load) or custom actions you define on this form.
7470      * @param {String} actionName The name of the action type
7471      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7472      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7473      * accept other config options):
7474      * <pre>
7475 Property          Type             Description
7476 ----------------  ---------------  ----------------------------------------------------------------------------------
7477 url               String           The url for the action (defaults to the form's url)
7478 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7479 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7480 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7481                                    validate the form on the client (defaults to false)
7482      * </pre>
7483      * @return {BasicForm} this
7484      */
7485     doAction : function(action, options){
7486         if(typeof action == 'string'){
7487             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7488         }
7489         if(this.fireEvent('beforeaction', this, action) !== false){
7490             this.beforeAction(action);
7491             action.run.defer(100, action);
7492         }
7493         return this;
7494     },
7495     
7496     // private
7497     beforeAction : function(action){
7498         var o = action.options;
7499         
7500         if(this.loadMask){
7501             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7502         }
7503         // not really supported yet.. ??
7504         
7505         //if(this.waitMsgTarget === true){
7506         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7507         //}else if(this.waitMsgTarget){
7508         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7509         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7510         //}else {
7511         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7512        // }
7513          
7514     },
7515
7516     // private
7517     afterAction : function(action, success){
7518         this.activeAction = null;
7519         var o = action.options;
7520         
7521         //if(this.waitMsgTarget === true){
7522             this.el.unmask();
7523         //}else if(this.waitMsgTarget){
7524         //    this.waitMsgTarget.unmask();
7525         //}else{
7526         //    Roo.MessageBox.updateProgress(1);
7527         //    Roo.MessageBox.hide();
7528        // }
7529         // 
7530         if(success){
7531             if(o.reset){
7532                 this.reset();
7533             }
7534             Roo.callback(o.success, o.scope, [this, action]);
7535             this.fireEvent('actioncomplete', this, action);
7536             
7537         }else{
7538             
7539             // failure condition..
7540             // we have a scenario where updates need confirming.
7541             // eg. if a locking scenario exists..
7542             // we look for { errors : { needs_confirm : true }} in the response.
7543             if (
7544                 (typeof(action.result) != 'undefined')  &&
7545                 (typeof(action.result.errors) != 'undefined')  &&
7546                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7547            ){
7548                 var _t = this;
7549                 Roo.log("not supported yet");
7550                  /*
7551                 
7552                 Roo.MessageBox.confirm(
7553                     "Change requires confirmation",
7554                     action.result.errorMsg,
7555                     function(r) {
7556                         if (r != 'yes') {
7557                             return;
7558                         }
7559                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7560                     }
7561                     
7562                 );
7563                 */
7564                 
7565                 
7566                 return;
7567             }
7568             
7569             Roo.callback(o.failure, o.scope, [this, action]);
7570             // show an error message if no failed handler is set..
7571             if (!this.hasListener('actionfailed')) {
7572                 Roo.log("need to add dialog support");
7573                 /*
7574                 Roo.MessageBox.alert("Error",
7575                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7576                         action.result.errorMsg :
7577                         "Saving Failed, please check your entries or try again"
7578                 );
7579                 */
7580             }
7581             
7582             this.fireEvent('actionfailed', this, action);
7583         }
7584         
7585     },
7586     /**
7587      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7588      * @param {String} id The value to search for
7589      * @return Field
7590      */
7591     findField : function(id){
7592         var items = this.getItems();
7593         var field = items.get(id);
7594         if(!field){
7595              items.each(function(f){
7596                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7597                     field = f;
7598                     return false;
7599                 }
7600                 return true;
7601             });
7602         }
7603         return field || null;
7604     },
7605      /**
7606      * Mark fields in this form invalid in bulk.
7607      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7608      * @return {BasicForm} this
7609      */
7610     markInvalid : function(errors){
7611         if(errors instanceof Array){
7612             for(var i = 0, len = errors.length; i < len; i++){
7613                 var fieldError = errors[i];
7614                 var f = this.findField(fieldError.id);
7615                 if(f){
7616                     f.markInvalid(fieldError.msg);
7617                 }
7618             }
7619         }else{
7620             var field, id;
7621             for(id in errors){
7622                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7623                     field.markInvalid(errors[id]);
7624                 }
7625             }
7626         }
7627         //Roo.each(this.childForms || [], function (f) {
7628         //    f.markInvalid(errors);
7629         //});
7630         
7631         return this;
7632     },
7633
7634     /**
7635      * Set values for fields in this form in bulk.
7636      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7637      * @return {BasicForm} this
7638      */
7639     setValues : function(values){
7640         if(values instanceof Array){ // array of objects
7641             for(var i = 0, len = values.length; i < len; i++){
7642                 var v = values[i];
7643                 var f = this.findField(v.id);
7644                 if(f){
7645                     f.setValue(v.value);
7646                     if(this.trackResetOnLoad){
7647                         f.originalValue = f.getValue();
7648                     }
7649                 }
7650             }
7651         }else{ // object hash
7652             var field, id;
7653             for(id in values){
7654                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7655                     
7656                     if (field.setFromData && 
7657                         field.valueField && 
7658                         field.displayField &&
7659                         // combos' with local stores can 
7660                         // be queried via setValue()
7661                         // to set their value..
7662                         (field.store && !field.store.isLocal)
7663                         ) {
7664                         // it's a combo
7665                         var sd = { };
7666                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7667                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7668                         field.setFromData(sd);
7669                         
7670                     } else {
7671                         field.setValue(values[id]);
7672                     }
7673                     
7674                     
7675                     if(this.trackResetOnLoad){
7676                         field.originalValue = field.getValue();
7677                     }
7678                 }
7679             }
7680         }
7681          
7682         //Roo.each(this.childForms || [], function (f) {
7683         //    f.setValues(values);
7684         //});
7685                 
7686         return this;
7687     },
7688
7689     /**
7690      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7691      * they are returned as an array.
7692      * @param {Boolean} asString
7693      * @return {Object}
7694      */
7695     getValues : function(asString){
7696         //if (this.childForms) {
7697             // copy values from the child forms
7698         //    Roo.each(this.childForms, function (f) {
7699         //        this.setValues(f.getValues());
7700         //    }, this);
7701         //}
7702         
7703         
7704         
7705         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7706         if(asString === true){
7707             return fs;
7708         }
7709         return Roo.urlDecode(fs);
7710     },
7711     
7712     /**
7713      * Returns the fields in this form as an object with key/value pairs. 
7714      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7715      * @return {Object}
7716      */
7717     getFieldValues : function(with_hidden)
7718     {
7719         var items = this.getItems();
7720         var ret = {};
7721         items.each(function(f){
7722             if (!f.getName()) {
7723                 return;
7724             }
7725             var v = f.getValue();
7726             if (f.inputType =='radio') {
7727                 if (typeof(ret[f.getName()]) == 'undefined') {
7728                     ret[f.getName()] = ''; // empty..
7729                 }
7730                 
7731                 if (!f.el.dom.checked) {
7732                     return;
7733                     
7734                 }
7735                 v = f.el.dom.value;
7736                 
7737             }
7738             
7739             // not sure if this supported any more..
7740             if ((typeof(v) == 'object') && f.getRawValue) {
7741                 v = f.getRawValue() ; // dates..
7742             }
7743             // combo boxes where name != hiddenName...
7744             if (f.name != f.getName()) {
7745                 ret[f.name] = f.getRawValue();
7746             }
7747             ret[f.getName()] = v;
7748         });
7749         
7750         return ret;
7751     },
7752
7753     /**
7754      * Clears all invalid messages in this form.
7755      * @return {BasicForm} this
7756      */
7757     clearInvalid : function(){
7758         var items = this.getItems();
7759         
7760         items.each(function(f){
7761            f.clearInvalid();
7762         });
7763         
7764         
7765         
7766         return this;
7767     },
7768
7769     /**
7770      * Resets this form.
7771      * @return {BasicForm} this
7772      */
7773     reset : function(){
7774         var items = this.getItems();
7775         items.each(function(f){
7776             f.reset();
7777         });
7778         
7779         Roo.each(this.childForms || [], function (f) {
7780             f.reset();
7781         });
7782        
7783         
7784         return this;
7785     },
7786     getItems : function()
7787     {
7788         var r=new Roo.util.MixedCollection(false, function(o){
7789             return o.id || (o.id = Roo.id());
7790         });
7791         var iter = function(el) {
7792             if (el.inputEl) {
7793                 r.add(el);
7794             }
7795             if (!el.items) {
7796                 return;
7797             }
7798             Roo.each(el.items,function(e) {
7799                 iter(e);
7800             });
7801             
7802             
7803         };
7804         
7805         iter(this);
7806         return r;
7807         
7808         
7809         
7810         
7811     }
7812     
7813 });
7814
7815  
7816 /*
7817  * Based on:
7818  * Ext JS Library 1.1.1
7819  * Copyright(c) 2006-2007, Ext JS, LLC.
7820  *
7821  * Originally Released Under LGPL - original licence link has changed is not relivant.
7822  *
7823  * Fork - LGPL
7824  * <script type="text/javascript">
7825  */
7826 /**
7827  * @class Roo.form.VTypes
7828  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7829  * @singleton
7830  */
7831 Roo.form.VTypes = function(){
7832     // closure these in so they are only created once.
7833     var alpha = /^[a-zA-Z_]+$/;
7834     var alphanum = /^[a-zA-Z0-9_]+$/;
7835     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7836     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7837
7838     // All these messages and functions are configurable
7839     return {
7840         /**
7841          * The function used to validate email addresses
7842          * @param {String} value The email address
7843          */
7844         'email' : function(v){
7845             return email.test(v);
7846         },
7847         /**
7848          * The error text to display when the email validation function returns false
7849          * @type String
7850          */
7851         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7852         /**
7853          * The keystroke filter mask to be applied on email input
7854          * @type RegExp
7855          */
7856         'emailMask' : /[a-z0-9_\.\-@]/i,
7857
7858         /**
7859          * The function used to validate URLs
7860          * @param {String} value The URL
7861          */
7862         'url' : function(v){
7863             return url.test(v);
7864         },
7865         /**
7866          * The error text to display when the url validation function returns false
7867          * @type String
7868          */
7869         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7870         
7871         /**
7872          * The function used to validate alpha values
7873          * @param {String} value The value
7874          */
7875         'alpha' : function(v){
7876             return alpha.test(v);
7877         },
7878         /**
7879          * The error text to display when the alpha validation function returns false
7880          * @type String
7881          */
7882         'alphaText' : 'This field should only contain letters and _',
7883         /**
7884          * The keystroke filter mask to be applied on alpha input
7885          * @type RegExp
7886          */
7887         'alphaMask' : /[a-z_]/i,
7888
7889         /**
7890          * The function used to validate alphanumeric values
7891          * @param {String} value The value
7892          */
7893         'alphanum' : function(v){
7894             return alphanum.test(v);
7895         },
7896         /**
7897          * The error text to display when the alphanumeric validation function returns false
7898          * @type String
7899          */
7900         'alphanumText' : 'This field should only contain letters, numbers and _',
7901         /**
7902          * The keystroke filter mask to be applied on alphanumeric input
7903          * @type RegExp
7904          */
7905         'alphanumMask' : /[a-z0-9_]/i
7906     };
7907 }();/*
7908  * - LGPL
7909  *
7910  * Input
7911  * 
7912  */
7913
7914 /**
7915  * @class Roo.bootstrap.Input
7916  * @extends Roo.bootstrap.Component
7917  * Bootstrap Input class
7918  * @cfg {Boolean} disabled is it disabled
7919  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7920  * @cfg {String} name name of the input
7921  * @cfg {string} fieldLabel - the label associated
7922  * @cfg {string} placeholder - placeholder to put in text.
7923  * @cfg {string}  before - input group add on before
7924  * @cfg {string} after - input group add on after
7925  * @cfg {string} size - (lg|sm) or leave empty..
7926  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7927  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7928  * @cfg {Number} md colspan out of 12 for computer-sized screens
7929  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7930  * @cfg {string} value default value of the input
7931  * @cfg {Number} labelWidth set the width of label (0-12)
7932  * @cfg {String} labelAlign (top|left)
7933  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7934  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7935
7936  * @cfg {String} align (left|center|right) Default left
7937  * @cfg {Boolean} forceFeedback (true|false) Default false
7938  * 
7939  * 
7940  * 
7941  * 
7942  * @constructor
7943  * Create a new Input
7944  * @param {Object} config The config object
7945  */
7946
7947 Roo.bootstrap.Input = function(config){
7948     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7949    
7950         this.addEvents({
7951             /**
7952              * @event focus
7953              * Fires when this field receives input focus.
7954              * @param {Roo.form.Field} this
7955              */
7956             focus : true,
7957             /**
7958              * @event blur
7959              * Fires when this field loses input focus.
7960              * @param {Roo.form.Field} this
7961              */
7962             blur : true,
7963             /**
7964              * @event specialkey
7965              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7966              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7967              * @param {Roo.form.Field} this
7968              * @param {Roo.EventObject} e The event object
7969              */
7970             specialkey : true,
7971             /**
7972              * @event change
7973              * Fires just before the field blurs if the field value has changed.
7974              * @param {Roo.form.Field} this
7975              * @param {Mixed} newValue The new value
7976              * @param {Mixed} oldValue The original value
7977              */
7978             change : true,
7979             /**
7980              * @event invalid
7981              * Fires after the field has been marked as invalid.
7982              * @param {Roo.form.Field} this
7983              * @param {String} msg The validation message
7984              */
7985             invalid : true,
7986             /**
7987              * @event valid
7988              * Fires after the field has been validated with no errors.
7989              * @param {Roo.form.Field} this
7990              */
7991             valid : true,
7992              /**
7993              * @event keyup
7994              * Fires after the key up
7995              * @param {Roo.form.Field} this
7996              * @param {Roo.EventObject}  e The event Object
7997              */
7998             keyup : true
7999         });
8000 };
8001
8002 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8003      /**
8004      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8005       automatic validation (defaults to "keyup").
8006      */
8007     validationEvent : "keyup",
8008      /**
8009      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8010      */
8011     validateOnBlur : true,
8012     /**
8013      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8014      */
8015     validationDelay : 250,
8016      /**
8017      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8018      */
8019     focusClass : "x-form-focus",  // not needed???
8020     
8021        
8022     /**
8023      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8024      */
8025     invalidClass : "has-warning",
8026     
8027     /**
8028      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8029      */
8030     validClass : "has-success",
8031     
8032     /**
8033      * @cfg {Boolean} hasFeedback (true|false) default true
8034      */
8035     hasFeedback : true,
8036     
8037     /**
8038      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8039      */
8040     invalidFeedbackClass : "glyphicon-warning-sign",
8041     
8042     /**
8043      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8044      */
8045     validFeedbackClass : "glyphicon-ok",
8046     
8047     /**
8048      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8049      */
8050     selectOnFocus : false,
8051     
8052      /**
8053      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8054      */
8055     maskRe : null,
8056        /**
8057      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8058      */
8059     vtype : null,
8060     
8061       /**
8062      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8063      */
8064     disableKeyFilter : false,
8065     
8066        /**
8067      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8068      */
8069     disabled : false,
8070      /**
8071      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8072      */
8073     allowBlank : true,
8074     /**
8075      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8076      */
8077     blankText : "This field is required",
8078     
8079      /**
8080      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8081      */
8082     minLength : 0,
8083     /**
8084      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8085      */
8086     maxLength : Number.MAX_VALUE,
8087     /**
8088      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8089      */
8090     minLengthText : "The minimum length for this field is {0}",
8091     /**
8092      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8093      */
8094     maxLengthText : "The maximum length for this field is {0}",
8095   
8096     
8097     /**
8098      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8099      * If available, this function will be called only after the basic validators all return true, and will be passed the
8100      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8101      */
8102     validator : null,
8103     /**
8104      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8105      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8106      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8107      */
8108     regex : null,
8109     /**
8110      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8111      */
8112     regexText : "",
8113     
8114     autocomplete: false,
8115     
8116     
8117     fieldLabel : '',
8118     inputType : 'text',
8119     
8120     name : false,
8121     placeholder: false,
8122     before : false,
8123     after : false,
8124     size : false,
8125     hasFocus : false,
8126     preventMark: false,
8127     isFormField : true,
8128     value : '',
8129     labelWidth : 2,
8130     labelAlign : false,
8131     readOnly : false,
8132     align : false,
8133     formatedValue : false,
8134     forceFeedback : false,
8135     
8136     parentLabelAlign : function()
8137     {
8138         var parent = this;
8139         while (parent.parent()) {
8140             parent = parent.parent();
8141             if (typeof(parent.labelAlign) !='undefined') {
8142                 return parent.labelAlign;
8143             }
8144         }
8145         return 'left';
8146         
8147     },
8148     
8149     getAutoCreate : function(){
8150         
8151         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8152         
8153         var id = Roo.id();
8154         
8155         var cfg = {};
8156         
8157         if(this.inputType != 'hidden'){
8158             cfg.cls = 'form-group' //input-group
8159         }
8160         
8161         var input =  {
8162             tag: 'input',
8163             id : id,
8164             type : this.inputType,
8165             value : this.value,
8166             cls : 'form-control',
8167             placeholder : this.placeholder || '',
8168             autocomplete : this.autocomplete || 'new-password'
8169         };
8170         
8171         
8172         if(this.align){
8173             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8174         }
8175         
8176         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8177             input.maxLength = this.maxLength;
8178         }
8179         
8180         if (this.disabled) {
8181             input.disabled=true;
8182         }
8183         
8184         if (this.readOnly) {
8185             input.readonly=true;
8186         }
8187         
8188         if (this.name) {
8189             input.name = this.name;
8190         }
8191         if (this.size) {
8192             input.cls += ' input-' + this.size;
8193         }
8194         var settings=this;
8195         ['xs','sm','md','lg'].map(function(size){
8196             if (settings[size]) {
8197                 cfg.cls += ' col-' + size + '-' + settings[size];
8198             }
8199         });
8200         
8201         var inputblock = input;
8202         
8203         var feedback = {
8204             tag: 'span',
8205             cls: 'glyphicon form-control-feedback'
8206         };
8207             
8208         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8209             
8210             inputblock = {
8211                 cls : 'has-feedback',
8212                 cn :  [
8213                     input,
8214                     feedback
8215                 ] 
8216             };  
8217         }
8218         
8219         if (this.before || this.after) {
8220             
8221             inputblock = {
8222                 cls : 'input-group',
8223                 cn :  [] 
8224             };
8225             
8226             if (this.before && typeof(this.before) == 'string') {
8227                 
8228                 inputblock.cn.push({
8229                     tag :'span',
8230                     cls : 'roo-input-before input-group-addon',
8231                     html : this.before
8232                 });
8233             }
8234             if (this.before && typeof(this.before) == 'object') {
8235                 this.before = Roo.factory(this.before);
8236                 
8237                 inputblock.cn.push({
8238                     tag :'span',
8239                     cls : 'roo-input-before input-group-' +
8240                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8241                 });
8242             }
8243             
8244             inputblock.cn.push(input);
8245             
8246             if (this.after && typeof(this.after) == 'string') {
8247                 inputblock.cn.push({
8248                     tag :'span',
8249                     cls : 'roo-input-after input-group-addon',
8250                     html : this.after
8251                 });
8252             }
8253             if (this.after && typeof(this.after) == 'object') {
8254                 this.after = Roo.factory(this.after);
8255                 
8256                 inputblock.cn.push({
8257                     tag :'span',
8258                     cls : 'roo-input-after input-group-' +
8259                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8260                 });
8261             }
8262             
8263             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8264                 inputblock.cls += ' has-feedback';
8265                 inputblock.cn.push(feedback);
8266             }
8267         };
8268         
8269         if (align ==='left' && this.fieldLabel.length) {
8270                 
8271                 cfg.cn = [
8272                     
8273                     {
8274                         tag: 'label',
8275                         'for' :  id,
8276                         cls : 'control-label col-sm-' + this.labelWidth,
8277                         html : this.fieldLabel
8278                         
8279                     },
8280                     {
8281                         cls : "col-sm-" + (12 - this.labelWidth), 
8282                         cn: [
8283                             inputblock
8284                         ]
8285                     }
8286                     
8287                 ];
8288         } else if ( this.fieldLabel.length) {
8289                 
8290                  cfg.cn = [
8291                    
8292                     {
8293                         tag: 'label',
8294                         //cls : 'input-group-addon',
8295                         html : this.fieldLabel
8296                         
8297                     },
8298                     
8299                     inputblock
8300                     
8301                 ];
8302
8303         } else {
8304             
8305                 cfg.cn = [
8306                     
8307                         inputblock
8308                     
8309                 ];
8310                 
8311                 
8312         };
8313         
8314         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8315            cfg.cls += ' navbar-form';
8316         }
8317         
8318         return cfg;
8319         
8320     },
8321     /**
8322      * return the real input element.
8323      */
8324     inputEl: function ()
8325     {
8326         return this.el.select('input.form-control',true).first();
8327     },
8328     
8329     tooltipEl : function()
8330     {
8331         return this.inputEl();
8332     },
8333     
8334     setDisabled : function(v)
8335     {
8336         var i  = this.inputEl().dom;
8337         if (!v) {
8338             i.removeAttribute('disabled');
8339             return;
8340             
8341         }
8342         i.setAttribute('disabled','true');
8343     },
8344     initEvents : function()
8345     {
8346           
8347         this.inputEl().on("keydown" , this.fireKey,  this);
8348         this.inputEl().on("focus", this.onFocus,  this);
8349         this.inputEl().on("blur", this.onBlur,  this);
8350         
8351         this.inputEl().relayEvent('keyup', this);
8352  
8353         // reference to original value for reset
8354         this.originalValue = this.getValue();
8355         //Roo.form.TextField.superclass.initEvents.call(this);
8356         if(this.validationEvent == 'keyup'){
8357             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8358             this.inputEl().on('keyup', this.filterValidation, this);
8359         }
8360         else if(this.validationEvent !== false){
8361             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8362         }
8363         
8364         if(this.selectOnFocus){
8365             this.on("focus", this.preFocus, this);
8366             
8367         }
8368         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8369             this.inputEl().on("keypress", this.filterKeys, this);
8370         }
8371        /* if(this.grow){
8372             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8373             this.el.on("click", this.autoSize,  this);
8374         }
8375         */
8376         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8377             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8378         }
8379         
8380         if (typeof(this.before) == 'object') {
8381             this.before.render(this.el.select('.roo-input-before',true).first());
8382         }
8383         if (typeof(this.after) == 'object') {
8384             this.after.render(this.el.select('.roo-input-after',true).first());
8385         }
8386         
8387         
8388     },
8389     filterValidation : function(e){
8390         if(!e.isNavKeyPress()){
8391             this.validationTask.delay(this.validationDelay);
8392         }
8393     },
8394      /**
8395      * Validates the field value
8396      * @return {Boolean} True if the value is valid, else false
8397      */
8398     validate : function(){
8399         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8400         if(this.disabled || this.validateValue(this.getRawValue())){
8401             this.markValid();
8402             return true;
8403         }
8404         
8405         this.markInvalid();
8406         return false;
8407     },
8408     
8409     
8410     /**
8411      * Validates a value according to the field's validation rules and marks the field as invalid
8412      * if the validation fails
8413      * @param {Mixed} value The value to validate
8414      * @return {Boolean} True if the value is valid, else false
8415      */
8416     validateValue : function(value){
8417         if(value.length < 1)  { // if it's blank
8418             if(this.allowBlank){
8419                 return true;
8420             }
8421             return false;
8422         }
8423         
8424         if(value.length < this.minLength){
8425             return false;
8426         }
8427         if(value.length > this.maxLength){
8428             return false;
8429         }
8430         if(this.vtype){
8431             var vt = Roo.form.VTypes;
8432             if(!vt[this.vtype](value, this)){
8433                 return false;
8434             }
8435         }
8436         if(typeof this.validator == "function"){
8437             var msg = this.validator(value);
8438             if(msg !== true){
8439                 return false;
8440             }
8441         }
8442         
8443         if(this.regex && !this.regex.test(value)){
8444             return false;
8445         }
8446         
8447         return true;
8448     },
8449
8450     
8451     
8452      // private
8453     fireKey : function(e){
8454         //Roo.log('field ' + e.getKey());
8455         if(e.isNavKeyPress()){
8456             this.fireEvent("specialkey", this, e);
8457         }
8458     },
8459     focus : function (selectText){
8460         if(this.rendered){
8461             this.inputEl().focus();
8462             if(selectText === true){
8463                 this.inputEl().dom.select();
8464             }
8465         }
8466         return this;
8467     } ,
8468     
8469     onFocus : function(){
8470         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8471            // this.el.addClass(this.focusClass);
8472         }
8473         if(!this.hasFocus){
8474             this.hasFocus = true;
8475             this.startValue = this.getValue();
8476             this.fireEvent("focus", this);
8477         }
8478     },
8479     
8480     beforeBlur : Roo.emptyFn,
8481
8482     
8483     // private
8484     onBlur : function(){
8485         this.beforeBlur();
8486         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8487             //this.el.removeClass(this.focusClass);
8488         }
8489         this.hasFocus = false;
8490         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8491             this.validate();
8492         }
8493         var v = this.getValue();
8494         if(String(v) !== String(this.startValue)){
8495             this.fireEvent('change', this, v, this.startValue);
8496         }
8497         this.fireEvent("blur", this);
8498     },
8499     
8500     /**
8501      * Resets the current field value to the originally loaded value and clears any validation messages
8502      */
8503     reset : function(){
8504         this.setValue(this.originalValue);
8505         this.validate();
8506     },
8507      /**
8508      * Returns the name of the field
8509      * @return {Mixed} name The name field
8510      */
8511     getName: function(){
8512         return this.name;
8513     },
8514      /**
8515      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8516      * @return {Mixed} value The field value
8517      */
8518     getValue : function(){
8519         
8520         var v = this.inputEl().getValue();
8521         
8522         return v;
8523     },
8524     /**
8525      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8526      * @return {Mixed} value The field value
8527      */
8528     getRawValue : function(){
8529         var v = this.inputEl().getValue();
8530         
8531         return v;
8532     },
8533     
8534     /**
8535      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8536      * @param {Mixed} value The value to set
8537      */
8538     setRawValue : function(v){
8539         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8540     },
8541     
8542     selectText : function(start, end){
8543         var v = this.getRawValue();
8544         if(v.length > 0){
8545             start = start === undefined ? 0 : start;
8546             end = end === undefined ? v.length : end;
8547             var d = this.inputEl().dom;
8548             if(d.setSelectionRange){
8549                 d.setSelectionRange(start, end);
8550             }else if(d.createTextRange){
8551                 var range = d.createTextRange();
8552                 range.moveStart("character", start);
8553                 range.moveEnd("character", v.length-end);
8554                 range.select();
8555             }
8556         }
8557     },
8558     
8559     /**
8560      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8561      * @param {Mixed} value The value to set
8562      */
8563     setValue : function(v){
8564         this.value = v;
8565         if(this.rendered){
8566             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8567             this.validate();
8568         }
8569     },
8570     
8571     /*
8572     processValue : function(value){
8573         if(this.stripCharsRe){
8574             var newValue = value.replace(this.stripCharsRe, '');
8575             if(newValue !== value){
8576                 this.setRawValue(newValue);
8577                 return newValue;
8578             }
8579         }
8580         return value;
8581     },
8582   */
8583     preFocus : function(){
8584         
8585         if(this.selectOnFocus){
8586             this.inputEl().dom.select();
8587         }
8588     },
8589     filterKeys : function(e){
8590         var k = e.getKey();
8591         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8592             return;
8593         }
8594         var c = e.getCharCode(), cc = String.fromCharCode(c);
8595         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8596             return;
8597         }
8598         if(!this.maskRe.test(cc)){
8599             e.stopEvent();
8600         }
8601     },
8602      /**
8603      * Clear any invalid styles/messages for this field
8604      */
8605     clearInvalid : function(){
8606         
8607         if(!this.el || this.preventMark){ // not rendered
8608             return;
8609         }
8610         
8611         var label = this.el.select('label', true).first();
8612         var icon = this.el.select('i.fa-star', true).first();
8613         
8614         if(label && icon){
8615             icon.remove();
8616         }
8617         
8618         this.el.removeClass(this.invalidClass);
8619         
8620         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8621             
8622             var feedback = this.el.select('.form-control-feedback', true).first();
8623             
8624             if(feedback){
8625                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8626             }
8627             
8628         }
8629         
8630         this.fireEvent('valid', this);
8631     },
8632     
8633      /**
8634      * Mark this field as valid
8635      */
8636     markValid : function()
8637     {
8638         if(!this.el  || this.preventMark){ // not rendered
8639             return;
8640         }
8641         
8642         this.el.removeClass([this.invalidClass, this.validClass]);
8643         
8644         var feedback = this.el.select('.form-control-feedback', true).first();
8645             
8646         if(feedback){
8647             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8648         }
8649
8650         if(this.disabled || this.allowBlank){
8651             return;
8652         }
8653         
8654         var formGroup = this.el.findParent('.form-group', false, true);
8655         
8656         if(formGroup){
8657             
8658             var label = formGroup.select('label', true).first();
8659             var icon = formGroup.select('i.fa-star', true).first();
8660             
8661             if(label && icon){
8662                 icon.remove();
8663             }
8664         }
8665         
8666         this.el.addClass(this.validClass);
8667         
8668         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8669             
8670             var feedback = this.el.select('.form-control-feedback', true).first();
8671             
8672             if(feedback){
8673                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8674                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8675             }
8676             
8677         }
8678         
8679         this.fireEvent('valid', this);
8680     },
8681     
8682      /**
8683      * Mark this field as invalid
8684      * @param {String} msg The validation message
8685      */
8686     markInvalid : function(msg)
8687     {
8688         if(!this.el  || this.preventMark){ // not rendered
8689             return;
8690         }
8691         
8692         this.el.removeClass([this.invalidClass, this.validClass]);
8693         
8694         var feedback = this.el.select('.form-control-feedback', true).first();
8695             
8696         if(feedback){
8697             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8698         }
8699
8700         if(this.disabled || this.allowBlank){
8701             return;
8702         }
8703         
8704         var formGroup = this.el.findParent('.form-group', false, true);
8705         
8706         if(formGroup){
8707             var label = formGroup.select('label', true).first();
8708             var icon = formGroup.select('i.fa-star', true).first();
8709
8710             if(!this.getValue().length && label && !icon){
8711                 this.el.findParent('.form-group', false, true).createChild({
8712                     tag : 'i',
8713                     cls : 'text-danger fa fa-lg fa-star',
8714                     tooltip : 'This field is required',
8715                     style : 'margin-right:5px;'
8716                 }, label, true);
8717             }
8718         }
8719         
8720         
8721         this.el.addClass(this.invalidClass);
8722         
8723         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8724             
8725             var feedback = this.el.select('.form-control-feedback', true).first();
8726             
8727             if(feedback){
8728                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8729                 
8730                 if(this.getValue().length || this.forceFeedback){
8731                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8732                 }
8733                 
8734             }
8735             
8736         }
8737         
8738         this.fireEvent('invalid', this, msg);
8739     },
8740     // private
8741     SafariOnKeyDown : function(event)
8742     {
8743         // this is a workaround for a password hang bug on chrome/ webkit.
8744         
8745         var isSelectAll = false;
8746         
8747         if(this.inputEl().dom.selectionEnd > 0){
8748             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8749         }
8750         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8751             event.preventDefault();
8752             this.setValue('');
8753             return;
8754         }
8755         
8756         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8757             
8758             event.preventDefault();
8759             // this is very hacky as keydown always get's upper case.
8760             //
8761             var cc = String.fromCharCode(event.getCharCode());
8762             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8763             
8764         }
8765     },
8766     adjustWidth : function(tag, w){
8767         tag = tag.toLowerCase();
8768         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8769             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8770                 if(tag == 'input'){
8771                     return w + 2;
8772                 }
8773                 if(tag == 'textarea'){
8774                     return w-2;
8775                 }
8776             }else if(Roo.isOpera){
8777                 if(tag == 'input'){
8778                     return w + 2;
8779                 }
8780                 if(tag == 'textarea'){
8781                     return w-2;
8782                 }
8783             }
8784         }
8785         return w;
8786     }
8787     
8788 });
8789
8790  
8791 /*
8792  * - LGPL
8793  *
8794  * Input
8795  * 
8796  */
8797
8798 /**
8799  * @class Roo.bootstrap.TextArea
8800  * @extends Roo.bootstrap.Input
8801  * Bootstrap TextArea class
8802  * @cfg {Number} cols Specifies the visible width of a text area
8803  * @cfg {Number} rows Specifies the visible number of lines in a text area
8804  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8805  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8806  * @cfg {string} html text
8807  * 
8808  * @constructor
8809  * Create a new TextArea
8810  * @param {Object} config The config object
8811  */
8812
8813 Roo.bootstrap.TextArea = function(config){
8814     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8815    
8816 };
8817
8818 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8819      
8820     cols : false,
8821     rows : 5,
8822     readOnly : false,
8823     warp : 'soft',
8824     resize : false,
8825     value: false,
8826     html: false,
8827     
8828     getAutoCreate : function(){
8829         
8830         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8831         
8832         var id = Roo.id();
8833         
8834         var cfg = {};
8835         
8836         var input =  {
8837             tag: 'textarea',
8838             id : id,
8839             warp : this.warp,
8840             rows : this.rows,
8841             value : this.value || '',
8842             html: this.html || '',
8843             cls : 'form-control',
8844             placeholder : this.placeholder || '' 
8845             
8846         };
8847         
8848         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8849             input.maxLength = this.maxLength;
8850         }
8851         
8852         if(this.resize){
8853             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8854         }
8855         
8856         if(this.cols){
8857             input.cols = this.cols;
8858         }
8859         
8860         if (this.readOnly) {
8861             input.readonly = true;
8862         }
8863         
8864         if (this.name) {
8865             input.name = this.name;
8866         }
8867         
8868         if (this.size) {
8869             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8870         }
8871         
8872         var settings=this;
8873         ['xs','sm','md','lg'].map(function(size){
8874             if (settings[size]) {
8875                 cfg.cls += ' col-' + size + '-' + settings[size];
8876             }
8877         });
8878         
8879         var inputblock = input;
8880         
8881         if(this.hasFeedback && !this.allowBlank){
8882             
8883             var feedback = {
8884                 tag: 'span',
8885                 cls: 'glyphicon form-control-feedback'
8886             };
8887
8888             inputblock = {
8889                 cls : 'has-feedback',
8890                 cn :  [
8891                     input,
8892                     feedback
8893                 ] 
8894             };  
8895         }
8896         
8897         
8898         if (this.before || this.after) {
8899             
8900             inputblock = {
8901                 cls : 'input-group',
8902                 cn :  [] 
8903             };
8904             if (this.before) {
8905                 inputblock.cn.push({
8906                     tag :'span',
8907                     cls : 'input-group-addon',
8908                     html : this.before
8909                 });
8910             }
8911             
8912             inputblock.cn.push(input);
8913             
8914             if(this.hasFeedback && !this.allowBlank){
8915                 inputblock.cls += ' has-feedback';
8916                 inputblock.cn.push(feedback);
8917             }
8918             
8919             if (this.after) {
8920                 inputblock.cn.push({
8921                     tag :'span',
8922                     cls : 'input-group-addon',
8923                     html : this.after
8924                 });
8925             }
8926             
8927         }
8928         
8929         if (align ==='left' && this.fieldLabel.length) {
8930 //                Roo.log("left and has label");
8931                 cfg.cn = [
8932                     
8933                     {
8934                         tag: 'label',
8935                         'for' :  id,
8936                         cls : 'control-label col-sm-' + this.labelWidth,
8937                         html : this.fieldLabel
8938                         
8939                     },
8940                     {
8941                         cls : "col-sm-" + (12 - this.labelWidth), 
8942                         cn: [
8943                             inputblock
8944                         ]
8945                     }
8946                     
8947                 ];
8948         } else if ( this.fieldLabel.length) {
8949 //                Roo.log(" label");
8950                  cfg.cn = [
8951                    
8952                     {
8953                         tag: 'label',
8954                         //cls : 'input-group-addon',
8955                         html : this.fieldLabel
8956                         
8957                     },
8958                     
8959                     inputblock
8960                     
8961                 ];
8962
8963         } else {
8964             
8965 //                   Roo.log(" no label && no align");
8966                 cfg.cn = [
8967                     
8968                         inputblock
8969                     
8970                 ];
8971                 
8972                 
8973         }
8974         
8975         if (this.disabled) {
8976             input.disabled=true;
8977         }
8978         
8979         return cfg;
8980         
8981     },
8982     /**
8983      * return the real textarea element.
8984      */
8985     inputEl: function ()
8986     {
8987         return this.el.select('textarea.form-control',true).first();
8988     },
8989     
8990     /**
8991      * Clear any invalid styles/messages for this field
8992      */
8993     clearInvalid : function()
8994     {
8995         
8996         if(!this.el || this.preventMark){ // not rendered
8997             return;
8998         }
8999         
9000         var label = this.el.select('label', true).first();
9001         var icon = this.el.select('i.fa-star', true).first();
9002         
9003         if(label && icon){
9004             icon.remove();
9005         }
9006         
9007         this.el.removeClass(this.invalidClass);
9008         
9009         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9010             
9011             var feedback = this.el.select('.form-control-feedback', true).first();
9012             
9013             if(feedback){
9014                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9015             }
9016             
9017         }
9018         
9019         this.fireEvent('valid', this);
9020     },
9021     
9022      /**
9023      * Mark this field as valid
9024      */
9025     markValid : function()
9026     {
9027         if(!this.el  || this.preventMark){ // not rendered
9028             return;
9029         }
9030         
9031         this.el.removeClass([this.invalidClass, this.validClass]);
9032         
9033         var feedback = this.el.select('.form-control-feedback', true).first();
9034             
9035         if(feedback){
9036             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9037         }
9038
9039         if(this.disabled || this.allowBlank){
9040             return;
9041         }
9042         
9043         var label = this.el.select('label', true).first();
9044         var icon = this.el.select('i.fa-star', true).first();
9045         
9046         if(label && icon){
9047             icon.remove();
9048         }
9049         
9050         this.el.addClass(this.validClass);
9051         
9052         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9053             
9054             var feedback = this.el.select('.form-control-feedback', true).first();
9055             
9056             if(feedback){
9057                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9058                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9059             }
9060             
9061         }
9062         
9063         this.fireEvent('valid', this);
9064     },
9065     
9066      /**
9067      * Mark this field as invalid
9068      * @param {String} msg The validation message
9069      */
9070     markInvalid : function(msg)
9071     {
9072         if(!this.el  || this.preventMark){ // not rendered
9073             return;
9074         }
9075         
9076         this.el.removeClass([this.invalidClass, this.validClass]);
9077         
9078         var feedback = this.el.select('.form-control-feedback', true).first();
9079             
9080         if(feedback){
9081             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9082         }
9083
9084         if(this.disabled || this.allowBlank){
9085             return;
9086         }
9087         
9088         var label = this.el.select('label', true).first();
9089         var icon = this.el.select('i.fa-star', true).first();
9090         
9091         if(!this.getValue().length && label && !icon){
9092             this.el.createChild({
9093                 tag : 'i',
9094                 cls : 'text-danger fa fa-lg fa-star',
9095                 tooltip : 'This field is required',
9096                 style : 'margin-right:5px;'
9097             }, label, true);
9098         }
9099
9100         this.el.addClass(this.invalidClass);
9101         
9102         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9103             
9104             var feedback = this.el.select('.form-control-feedback', true).first();
9105             
9106             if(feedback){
9107                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9108                 
9109                 if(this.getValue().length || this.forceFeedback){
9110                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9111                 }
9112                 
9113             }
9114             
9115         }
9116         
9117         this.fireEvent('invalid', this, msg);
9118     }
9119 });
9120
9121  
9122 /*
9123  * - LGPL
9124  *
9125  * trigger field - base class for combo..
9126  * 
9127  */
9128  
9129 /**
9130  * @class Roo.bootstrap.TriggerField
9131  * @extends Roo.bootstrap.Input
9132  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9133  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9134  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9135  * for which you can provide a custom implementation.  For example:
9136  * <pre><code>
9137 var trigger = new Roo.bootstrap.TriggerField();
9138 trigger.onTriggerClick = myTriggerFn;
9139 trigger.applyTo('my-field');
9140 </code></pre>
9141  *
9142  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9143  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9144  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9145  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9146  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9147
9148  * @constructor
9149  * Create a new TriggerField.
9150  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9151  * to the base TextField)
9152  */
9153 Roo.bootstrap.TriggerField = function(config){
9154     this.mimicing = false;
9155     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9156 };
9157
9158 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9159     /**
9160      * @cfg {String} triggerClass A CSS class to apply to the trigger
9161      */
9162      /**
9163      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9164      */
9165     hideTrigger:false,
9166
9167     /**
9168      * @cfg {Boolean} removable (true|false) special filter default false
9169      */
9170     removable : false,
9171     
9172     /** @cfg {Boolean} grow @hide */
9173     /** @cfg {Number} growMin @hide */
9174     /** @cfg {Number} growMax @hide */
9175
9176     /**
9177      * @hide 
9178      * @method
9179      */
9180     autoSize: Roo.emptyFn,
9181     // private
9182     monitorTab : true,
9183     // private
9184     deferHeight : true,
9185
9186     
9187     actionMode : 'wrap',
9188     
9189     caret : false,
9190     
9191     
9192     getAutoCreate : function(){
9193        
9194         var align = this.labelAlign || this.parentLabelAlign();
9195         
9196         var id = Roo.id();
9197         
9198         var cfg = {
9199             cls: 'form-group' //input-group
9200         };
9201         
9202         
9203         var input =  {
9204             tag: 'input',
9205             id : id,
9206             type : this.inputType,
9207             cls : 'form-control',
9208             autocomplete: 'new-password',
9209             placeholder : this.placeholder || '' 
9210             
9211         };
9212         if (this.name) {
9213             input.name = this.name;
9214         }
9215         if (this.size) {
9216             input.cls += ' input-' + this.size;
9217         }
9218         
9219         if (this.disabled) {
9220             input.disabled=true;
9221         }
9222         
9223         var inputblock = input;
9224         
9225         if(this.hasFeedback && !this.allowBlank){
9226             
9227             var feedback = {
9228                 tag: 'span',
9229                 cls: 'glyphicon form-control-feedback'
9230             };
9231             
9232             if(this.removable && !this.editable && !this.tickable){
9233                 inputblock = {
9234                     cls : 'has-feedback',
9235                     cn :  [
9236                         inputblock,
9237                         {
9238                             tag: 'button',
9239                             html : 'x',
9240                             cls : 'roo-combo-removable-btn close'
9241                         },
9242                         feedback
9243                     ] 
9244                 };
9245             } else {
9246                 inputblock = {
9247                     cls : 'has-feedback',
9248                     cn :  [
9249                         inputblock,
9250                         feedback
9251                     ] 
9252                 };
9253             }
9254
9255         } else {
9256             if(this.removable && !this.editable && !this.tickable){
9257                 inputblock = {
9258                     cls : 'roo-removable',
9259                     cn :  [
9260                         inputblock,
9261                         {
9262                             tag: 'button',
9263                             html : 'x',
9264                             cls : 'roo-combo-removable-btn close'
9265                         }
9266                     ] 
9267                 };
9268             }
9269         }
9270         
9271         if (this.before || this.after) {
9272             
9273             inputblock = {
9274                 cls : 'input-group',
9275                 cn :  [] 
9276             };
9277             if (this.before) {
9278                 inputblock.cn.push({
9279                     tag :'span',
9280                     cls : 'input-group-addon',
9281                     html : this.before
9282                 });
9283             }
9284             
9285             inputblock.cn.push(input);
9286             
9287             if(this.hasFeedback && !this.allowBlank){
9288                 inputblock.cls += ' has-feedback';
9289                 inputblock.cn.push(feedback);
9290             }
9291             
9292             if (this.after) {
9293                 inputblock.cn.push({
9294                     tag :'span',
9295                     cls : 'input-group-addon',
9296                     html : this.after
9297                 });
9298             }
9299             
9300         };
9301         
9302         var box = {
9303             tag: 'div',
9304             cn: [
9305                 {
9306                     tag: 'input',
9307                     type : 'hidden',
9308                     cls: 'form-hidden-field'
9309                 },
9310                 inputblock
9311             ]
9312             
9313         };
9314         
9315         if(this.multiple){
9316             box = {
9317                 tag: 'div',
9318                 cn: [
9319                     {
9320                         tag: 'input',
9321                         type : 'hidden',
9322                         cls: 'form-hidden-field'
9323                     },
9324                     {
9325                         tag: 'ul',
9326                         cls: 'roo-select2-choices',
9327                         cn:[
9328                             {
9329                                 tag: 'li',
9330                                 cls: 'roo-select2-search-field',
9331                                 cn: [
9332
9333                                     inputblock
9334                                 ]
9335                             }
9336                         ]
9337                     }
9338                 ]
9339             }
9340         };
9341         
9342         var combobox = {
9343             cls: 'roo-select2-container input-group',
9344             cn: [
9345                 box
9346 //                {
9347 //                    tag: 'ul',
9348 //                    cls: 'typeahead typeahead-long dropdown-menu',
9349 //                    style: 'display:none'
9350 //                }
9351             ]
9352         };
9353         
9354         if(!this.multiple && this.showToggleBtn){
9355             
9356             var caret = {
9357                         tag: 'span',
9358                         cls: 'caret'
9359              };
9360             if (this.caret != false) {
9361                 caret = {
9362                      tag: 'i',
9363                      cls: 'fa fa-' + this.caret
9364                 };
9365                 
9366             }
9367             
9368             combobox.cn.push({
9369                 tag :'span',
9370                 cls : 'input-group-addon btn dropdown-toggle',
9371                 cn : [
9372                     caret,
9373                     {
9374                         tag: 'span',
9375                         cls: 'combobox-clear',
9376                         cn  : [
9377                             {
9378                                 tag : 'i',
9379                                 cls: 'icon-remove'
9380                             }
9381                         ]
9382                     }
9383                 ]
9384
9385             })
9386         }
9387         
9388         if(this.multiple){
9389             combobox.cls += ' roo-select2-container-multi';
9390         }
9391         
9392         if (align ==='left' && this.fieldLabel.length) {
9393             
9394 //                Roo.log("left and has label");
9395                 cfg.cn = [
9396                     
9397                     {
9398                         tag: 'label',
9399                         'for' :  id,
9400                         cls : 'control-label col-sm-' + this.labelWidth,
9401                         html : this.fieldLabel
9402                         
9403                     },
9404                     {
9405                         cls : "col-sm-" + (12 - this.labelWidth), 
9406                         cn: [
9407                             combobox
9408                         ]
9409                     }
9410                     
9411                 ];
9412         } else if ( this.fieldLabel.length) {
9413 //                Roo.log(" label");
9414                  cfg.cn = [
9415                    
9416                     {
9417                         tag: 'label',
9418                         //cls : 'input-group-addon',
9419                         html : this.fieldLabel
9420                         
9421                     },
9422                     
9423                     combobox
9424                     
9425                 ];
9426
9427         } else {
9428             
9429 //                Roo.log(" no label && no align");
9430                 cfg = combobox
9431                      
9432                 
9433         }
9434          
9435         var settings=this;
9436         ['xs','sm','md','lg'].map(function(size){
9437             if (settings[size]) {
9438                 cfg.cls += ' col-' + size + '-' + settings[size];
9439             }
9440         });
9441         
9442         return cfg;
9443         
9444     },
9445     
9446     
9447     
9448     // private
9449     onResize : function(w, h){
9450 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9451 //        if(typeof w == 'number'){
9452 //            var x = w - this.trigger.getWidth();
9453 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9454 //            this.trigger.setStyle('left', x+'px');
9455 //        }
9456     },
9457
9458     // private
9459     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9460
9461     // private
9462     getResizeEl : function(){
9463         return this.inputEl();
9464     },
9465
9466     // private
9467     getPositionEl : function(){
9468         return this.inputEl();
9469     },
9470
9471     // private
9472     alignErrorIcon : function(){
9473         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9474     },
9475
9476     // private
9477     initEvents : function(){
9478         
9479         this.createList();
9480         
9481         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9482         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9483         if(!this.multiple && this.showToggleBtn){
9484             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9485             if(this.hideTrigger){
9486                 this.trigger.setDisplayed(false);
9487             }
9488             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9489         }
9490         
9491         if(this.multiple){
9492             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9493         }
9494         
9495         if(this.removable && !this.editable && !this.tickable){
9496             var close = this.closeTriggerEl();
9497             
9498             if(close){
9499                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9500                 close.on('click', this.removeBtnClick, this, close);
9501             }
9502         }
9503         
9504         //this.trigger.addClassOnOver('x-form-trigger-over');
9505         //this.trigger.addClassOnClick('x-form-trigger-click');
9506         
9507         //if(!this.width){
9508         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9509         //}
9510     },
9511     
9512     closeTriggerEl : function()
9513     {
9514         var close = this.el.select('.roo-combo-removable-btn', true).first();
9515         return close ? close : false;
9516     },
9517     
9518     removeBtnClick : function(e, h, el)
9519     {
9520         e.preventDefault();
9521         
9522         if(this.fireEvent("remove", this) !== false){
9523             this.reset();
9524             this.fireEvent("afterremove", this)
9525         }
9526     },
9527     
9528     createList : function()
9529     {
9530         this.list = Roo.get(document.body).createChild({
9531             tag: 'ul',
9532             cls: 'typeahead typeahead-long dropdown-menu',
9533             style: 'display:none'
9534         });
9535         
9536         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9537         
9538     },
9539
9540     // private
9541     initTrigger : function(){
9542        
9543     },
9544
9545     // private
9546     onDestroy : function(){
9547         if(this.trigger){
9548             this.trigger.removeAllListeners();
9549           //  this.trigger.remove();
9550         }
9551         //if(this.wrap){
9552         //    this.wrap.remove();
9553         //}
9554         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9555     },
9556
9557     // private
9558     onFocus : function(){
9559         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9560         /*
9561         if(!this.mimicing){
9562             this.wrap.addClass('x-trigger-wrap-focus');
9563             this.mimicing = true;
9564             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9565             if(this.monitorTab){
9566                 this.el.on("keydown", this.checkTab, this);
9567             }
9568         }
9569         */
9570     },
9571
9572     // private
9573     checkTab : function(e){
9574         if(e.getKey() == e.TAB){
9575             this.triggerBlur();
9576         }
9577     },
9578
9579     // private
9580     onBlur : function(){
9581         // do nothing
9582     },
9583
9584     // private
9585     mimicBlur : function(e, t){
9586         /*
9587         if(!this.wrap.contains(t) && this.validateBlur()){
9588             this.triggerBlur();
9589         }
9590         */
9591     },
9592
9593     // private
9594     triggerBlur : function(){
9595         this.mimicing = false;
9596         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9597         if(this.monitorTab){
9598             this.el.un("keydown", this.checkTab, this);
9599         }
9600         //this.wrap.removeClass('x-trigger-wrap-focus');
9601         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9602     },
9603
9604     // private
9605     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9606     validateBlur : function(e, t){
9607         return true;
9608     },
9609
9610     // private
9611     onDisable : function(){
9612         this.inputEl().dom.disabled = true;
9613         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9614         //if(this.wrap){
9615         //    this.wrap.addClass('x-item-disabled');
9616         //}
9617     },
9618
9619     // private
9620     onEnable : function(){
9621         this.inputEl().dom.disabled = false;
9622         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9623         //if(this.wrap){
9624         //    this.el.removeClass('x-item-disabled');
9625         //}
9626     },
9627
9628     // private
9629     onShow : function(){
9630         var ae = this.getActionEl();
9631         
9632         if(ae){
9633             ae.dom.style.display = '';
9634             ae.dom.style.visibility = 'visible';
9635         }
9636     },
9637
9638     // private
9639     
9640     onHide : function(){
9641         var ae = this.getActionEl();
9642         ae.dom.style.display = 'none';
9643     },
9644
9645     /**
9646      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9647      * by an implementing function.
9648      * @method
9649      * @param {EventObject} e
9650      */
9651     onTriggerClick : Roo.emptyFn
9652 });
9653  /*
9654  * Based on:
9655  * Ext JS Library 1.1.1
9656  * Copyright(c) 2006-2007, Ext JS, LLC.
9657  *
9658  * Originally Released Under LGPL - original licence link has changed is not relivant.
9659  *
9660  * Fork - LGPL
9661  * <script type="text/javascript">
9662  */
9663
9664
9665 /**
9666  * @class Roo.data.SortTypes
9667  * @singleton
9668  * Defines the default sorting (casting?) comparison functions used when sorting data.
9669  */
9670 Roo.data.SortTypes = {
9671     /**
9672      * Default sort that does nothing
9673      * @param {Mixed} s The value being converted
9674      * @return {Mixed} The comparison value
9675      */
9676     none : function(s){
9677         return s;
9678     },
9679     
9680     /**
9681      * The regular expression used to strip tags
9682      * @type {RegExp}
9683      * @property
9684      */
9685     stripTagsRE : /<\/?[^>]+>/gi,
9686     
9687     /**
9688      * Strips all HTML tags to sort on text only
9689      * @param {Mixed} s The value being converted
9690      * @return {String} The comparison value
9691      */
9692     asText : function(s){
9693         return String(s).replace(this.stripTagsRE, "");
9694     },
9695     
9696     /**
9697      * Strips all HTML tags to sort on text only - Case insensitive
9698      * @param {Mixed} s The value being converted
9699      * @return {String} The comparison value
9700      */
9701     asUCText : function(s){
9702         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9703     },
9704     
9705     /**
9706      * Case insensitive string
9707      * @param {Mixed} s The value being converted
9708      * @return {String} The comparison value
9709      */
9710     asUCString : function(s) {
9711         return String(s).toUpperCase();
9712     },
9713     
9714     /**
9715      * Date sorting
9716      * @param {Mixed} s The value being converted
9717      * @return {Number} The comparison value
9718      */
9719     asDate : function(s) {
9720         if(!s){
9721             return 0;
9722         }
9723         if(s instanceof Date){
9724             return s.getTime();
9725         }
9726         return Date.parse(String(s));
9727     },
9728     
9729     /**
9730      * Float sorting
9731      * @param {Mixed} s The value being converted
9732      * @return {Float} The comparison value
9733      */
9734     asFloat : function(s) {
9735         var val = parseFloat(String(s).replace(/,/g, ""));
9736         if(isNaN(val)) {
9737             val = 0;
9738         }
9739         return val;
9740     },
9741     
9742     /**
9743      * Integer sorting
9744      * @param {Mixed} s The value being converted
9745      * @return {Number} The comparison value
9746      */
9747     asInt : function(s) {
9748         var val = parseInt(String(s).replace(/,/g, ""));
9749         if(isNaN(val)) {
9750             val = 0;
9751         }
9752         return val;
9753     }
9754 };/*
9755  * Based on:
9756  * Ext JS Library 1.1.1
9757  * Copyright(c) 2006-2007, Ext JS, LLC.
9758  *
9759  * Originally Released Under LGPL - original licence link has changed is not relivant.
9760  *
9761  * Fork - LGPL
9762  * <script type="text/javascript">
9763  */
9764
9765 /**
9766 * @class Roo.data.Record
9767  * Instances of this class encapsulate both record <em>definition</em> information, and record
9768  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9769  * to access Records cached in an {@link Roo.data.Store} object.<br>
9770  * <p>
9771  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9772  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9773  * objects.<br>
9774  * <p>
9775  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9776  * @constructor
9777  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9778  * {@link #create}. The parameters are the same.
9779  * @param {Array} data An associative Array of data values keyed by the field name.
9780  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9781  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9782  * not specified an integer id is generated.
9783  */
9784 Roo.data.Record = function(data, id){
9785     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9786     this.data = data;
9787 };
9788
9789 /**
9790  * Generate a constructor for a specific record layout.
9791  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9792  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9793  * Each field definition object may contain the following properties: <ul>
9794  * <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,
9795  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9796  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9797  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9798  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9799  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9800  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9801  * this may be omitted.</p></li>
9802  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9803  * <ul><li>auto (Default, implies no conversion)</li>
9804  * <li>string</li>
9805  * <li>int</li>
9806  * <li>float</li>
9807  * <li>boolean</li>
9808  * <li>date</li></ul></p></li>
9809  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9810  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9811  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9812  * by the Reader into an object that will be stored in the Record. It is passed the
9813  * following parameters:<ul>
9814  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9815  * </ul></p></li>
9816  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9817  * </ul>
9818  * <br>usage:<br><pre><code>
9819 var TopicRecord = Roo.data.Record.create(
9820     {name: 'title', mapping: 'topic_title'},
9821     {name: 'author', mapping: 'username'},
9822     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9823     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9824     {name: 'lastPoster', mapping: 'user2'},
9825     {name: 'excerpt', mapping: 'post_text'}
9826 );
9827
9828 var myNewRecord = new TopicRecord({
9829     title: 'Do my job please',
9830     author: 'noobie',
9831     totalPosts: 1,
9832     lastPost: new Date(),
9833     lastPoster: 'Animal',
9834     excerpt: 'No way dude!'
9835 });
9836 myStore.add(myNewRecord);
9837 </code></pre>
9838  * @method create
9839  * @static
9840  */
9841 Roo.data.Record.create = function(o){
9842     var f = function(){
9843         f.superclass.constructor.apply(this, arguments);
9844     };
9845     Roo.extend(f, Roo.data.Record);
9846     var p = f.prototype;
9847     p.fields = new Roo.util.MixedCollection(false, function(field){
9848         return field.name;
9849     });
9850     for(var i = 0, len = o.length; i < len; i++){
9851         p.fields.add(new Roo.data.Field(o[i]));
9852     }
9853     f.getField = function(name){
9854         return p.fields.get(name);  
9855     };
9856     return f;
9857 };
9858
9859 Roo.data.Record.AUTO_ID = 1000;
9860 Roo.data.Record.EDIT = 'edit';
9861 Roo.data.Record.REJECT = 'reject';
9862 Roo.data.Record.COMMIT = 'commit';
9863
9864 Roo.data.Record.prototype = {
9865     /**
9866      * Readonly flag - true if this record has been modified.
9867      * @type Boolean
9868      */
9869     dirty : false,
9870     editing : false,
9871     error: null,
9872     modified: null,
9873
9874     // private
9875     join : function(store){
9876         this.store = store;
9877     },
9878
9879     /**
9880      * Set the named field to the specified value.
9881      * @param {String} name The name of the field to set.
9882      * @param {Object} value The value to set the field to.
9883      */
9884     set : function(name, value){
9885         if(this.data[name] == value){
9886             return;
9887         }
9888         this.dirty = true;
9889         if(!this.modified){
9890             this.modified = {};
9891         }
9892         if(typeof this.modified[name] == 'undefined'){
9893             this.modified[name] = this.data[name];
9894         }
9895         this.data[name] = value;
9896         if(!this.editing && this.store){
9897             this.store.afterEdit(this);
9898         }       
9899     },
9900
9901     /**
9902      * Get the value of the named field.
9903      * @param {String} name The name of the field to get the value of.
9904      * @return {Object} The value of the field.
9905      */
9906     get : function(name){
9907         return this.data[name]; 
9908     },
9909
9910     // private
9911     beginEdit : function(){
9912         this.editing = true;
9913         this.modified = {}; 
9914     },
9915
9916     // private
9917     cancelEdit : function(){
9918         this.editing = false;
9919         delete this.modified;
9920     },
9921
9922     // private
9923     endEdit : function(){
9924         this.editing = false;
9925         if(this.dirty && this.store){
9926             this.store.afterEdit(this);
9927         }
9928     },
9929
9930     /**
9931      * Usually called by the {@link Roo.data.Store} which owns the Record.
9932      * Rejects all changes made to the Record since either creation, or the last commit operation.
9933      * Modified fields are reverted to their original values.
9934      * <p>
9935      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9936      * of reject operations.
9937      */
9938     reject : function(){
9939         var m = this.modified;
9940         for(var n in m){
9941             if(typeof m[n] != "function"){
9942                 this.data[n] = m[n];
9943             }
9944         }
9945         this.dirty = false;
9946         delete this.modified;
9947         this.editing = false;
9948         if(this.store){
9949             this.store.afterReject(this);
9950         }
9951     },
9952
9953     /**
9954      * Usually called by the {@link Roo.data.Store} which owns the Record.
9955      * Commits all changes made to the Record since either creation, or the last commit operation.
9956      * <p>
9957      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9958      * of commit operations.
9959      */
9960     commit : function(){
9961         this.dirty = false;
9962         delete this.modified;
9963         this.editing = false;
9964         if(this.store){
9965             this.store.afterCommit(this);
9966         }
9967     },
9968
9969     // private
9970     hasError : function(){
9971         return this.error != null;
9972     },
9973
9974     // private
9975     clearError : function(){
9976         this.error = null;
9977     },
9978
9979     /**
9980      * Creates a copy of this record.
9981      * @param {String} id (optional) A new record id if you don't want to use this record's id
9982      * @return {Record}
9983      */
9984     copy : function(newId) {
9985         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9986     }
9987 };/*
9988  * Based on:
9989  * Ext JS Library 1.1.1
9990  * Copyright(c) 2006-2007, Ext JS, LLC.
9991  *
9992  * Originally Released Under LGPL - original licence link has changed is not relivant.
9993  *
9994  * Fork - LGPL
9995  * <script type="text/javascript">
9996  */
9997
9998
9999
10000 /**
10001  * @class Roo.data.Store
10002  * @extends Roo.util.Observable
10003  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10004  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10005  * <p>
10006  * 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
10007  * has no knowledge of the format of the data returned by the Proxy.<br>
10008  * <p>
10009  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10010  * instances from the data object. These records are cached and made available through accessor functions.
10011  * @constructor
10012  * Creates a new Store.
10013  * @param {Object} config A config object containing the objects needed for the Store to access data,
10014  * and read the data into Records.
10015  */
10016 Roo.data.Store = function(config){
10017     this.data = new Roo.util.MixedCollection(false);
10018     this.data.getKey = function(o){
10019         return o.id;
10020     };
10021     this.baseParams = {};
10022     // private
10023     this.paramNames = {
10024         "start" : "start",
10025         "limit" : "limit",
10026         "sort" : "sort",
10027         "dir" : "dir",
10028         "multisort" : "_multisort"
10029     };
10030
10031     if(config && config.data){
10032         this.inlineData = config.data;
10033         delete config.data;
10034     }
10035
10036     Roo.apply(this, config);
10037     
10038     if(this.reader){ // reader passed
10039         this.reader = Roo.factory(this.reader, Roo.data);
10040         this.reader.xmodule = this.xmodule || false;
10041         if(!this.recordType){
10042             this.recordType = this.reader.recordType;
10043         }
10044         if(this.reader.onMetaChange){
10045             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10046         }
10047     }
10048
10049     if(this.recordType){
10050         this.fields = this.recordType.prototype.fields;
10051     }
10052     this.modified = [];
10053
10054     this.addEvents({
10055         /**
10056          * @event datachanged
10057          * Fires when the data cache has changed, and a widget which is using this Store
10058          * as a Record cache should refresh its view.
10059          * @param {Store} this
10060          */
10061         datachanged : true,
10062         /**
10063          * @event metachange
10064          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10065          * @param {Store} this
10066          * @param {Object} meta The JSON metadata
10067          */
10068         metachange : true,
10069         /**
10070          * @event add
10071          * Fires when Records have been added to the Store
10072          * @param {Store} this
10073          * @param {Roo.data.Record[]} records The array of Records added
10074          * @param {Number} index The index at which the record(s) were added
10075          */
10076         add : true,
10077         /**
10078          * @event remove
10079          * Fires when a Record has been removed from the Store
10080          * @param {Store} this
10081          * @param {Roo.data.Record} record The Record that was removed
10082          * @param {Number} index The index at which the record was removed
10083          */
10084         remove : true,
10085         /**
10086          * @event update
10087          * Fires when a Record has been updated
10088          * @param {Store} this
10089          * @param {Roo.data.Record} record The Record that was updated
10090          * @param {String} operation The update operation being performed.  Value may be one of:
10091          * <pre><code>
10092  Roo.data.Record.EDIT
10093  Roo.data.Record.REJECT
10094  Roo.data.Record.COMMIT
10095          * </code></pre>
10096          */
10097         update : true,
10098         /**
10099          * @event clear
10100          * Fires when the data cache has been cleared.
10101          * @param {Store} this
10102          */
10103         clear : true,
10104         /**
10105          * @event beforeload
10106          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10107          * the load action will be canceled.
10108          * @param {Store} this
10109          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10110          */
10111         beforeload : true,
10112         /**
10113          * @event beforeloadadd
10114          * Fires after a new set of Records has been loaded.
10115          * @param {Store} this
10116          * @param {Roo.data.Record[]} records The Records that were loaded
10117          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10118          */
10119         beforeloadadd : true,
10120         /**
10121          * @event load
10122          * Fires after a new set of Records has been loaded, before they are added to the store.
10123          * @param {Store} this
10124          * @param {Roo.data.Record[]} records The Records that were loaded
10125          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10126          * @params {Object} return from reader
10127          */
10128         load : true,
10129         /**
10130          * @event loadexception
10131          * Fires if an exception occurs in the Proxy during loading.
10132          * Called with the signature of the Proxy's "loadexception" event.
10133          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10134          * 
10135          * @param {Proxy} 
10136          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10137          * @param {Object} load options 
10138          * @param {Object} jsonData from your request (normally this contains the Exception)
10139          */
10140         loadexception : true
10141     });
10142     
10143     if(this.proxy){
10144         this.proxy = Roo.factory(this.proxy, Roo.data);
10145         this.proxy.xmodule = this.xmodule || false;
10146         this.relayEvents(this.proxy,  ["loadexception"]);
10147     }
10148     this.sortToggle = {};
10149     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10150
10151     Roo.data.Store.superclass.constructor.call(this);
10152
10153     if(this.inlineData){
10154         this.loadData(this.inlineData);
10155         delete this.inlineData;
10156     }
10157 };
10158
10159 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10160      /**
10161     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10162     * without a remote query - used by combo/forms at present.
10163     */
10164     
10165     /**
10166     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10167     */
10168     /**
10169     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10170     */
10171     /**
10172     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10173     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10174     */
10175     /**
10176     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10177     * on any HTTP request
10178     */
10179     /**
10180     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10181     */
10182     /**
10183     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10184     */
10185     multiSort: false,
10186     /**
10187     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10188     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10189     */
10190     remoteSort : false,
10191
10192     /**
10193     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10194      * loaded or when a record is removed. (defaults to false).
10195     */
10196     pruneModifiedRecords : false,
10197
10198     // private
10199     lastOptions : null,
10200
10201     /**
10202      * Add Records to the Store and fires the add event.
10203      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10204      */
10205     add : function(records){
10206         records = [].concat(records);
10207         for(var i = 0, len = records.length; i < len; i++){
10208             records[i].join(this);
10209         }
10210         var index = this.data.length;
10211         this.data.addAll(records);
10212         this.fireEvent("add", this, records, index);
10213     },
10214
10215     /**
10216      * Remove a Record from the Store and fires the remove event.
10217      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10218      */
10219     remove : function(record){
10220         var index = this.data.indexOf(record);
10221         this.data.removeAt(index);
10222         if(this.pruneModifiedRecords){
10223             this.modified.remove(record);
10224         }
10225         this.fireEvent("remove", this, record, index);
10226     },
10227
10228     /**
10229      * Remove all Records from the Store and fires the clear event.
10230      */
10231     removeAll : function(){
10232         this.data.clear();
10233         if(this.pruneModifiedRecords){
10234             this.modified = [];
10235         }
10236         this.fireEvent("clear", this);
10237     },
10238
10239     /**
10240      * Inserts Records to the Store at the given index and fires the add event.
10241      * @param {Number} index The start index at which to insert the passed Records.
10242      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10243      */
10244     insert : function(index, records){
10245         records = [].concat(records);
10246         for(var i = 0, len = records.length; i < len; i++){
10247             this.data.insert(index, records[i]);
10248             records[i].join(this);
10249         }
10250         this.fireEvent("add", this, records, index);
10251     },
10252
10253     /**
10254      * Get the index within the cache of the passed Record.
10255      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10256      * @return {Number} The index of the passed Record. Returns -1 if not found.
10257      */
10258     indexOf : function(record){
10259         return this.data.indexOf(record);
10260     },
10261
10262     /**
10263      * Get the index within the cache of the Record with the passed id.
10264      * @param {String} id The id of the Record to find.
10265      * @return {Number} The index of the Record. Returns -1 if not found.
10266      */
10267     indexOfId : function(id){
10268         return this.data.indexOfKey(id);
10269     },
10270
10271     /**
10272      * Get the Record with the specified id.
10273      * @param {String} id The id of the Record to find.
10274      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10275      */
10276     getById : function(id){
10277         return this.data.key(id);
10278     },
10279
10280     /**
10281      * Get the Record at the specified index.
10282      * @param {Number} index The index of the Record to find.
10283      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10284      */
10285     getAt : function(index){
10286         return this.data.itemAt(index);
10287     },
10288
10289     /**
10290      * Returns a range of Records between specified indices.
10291      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10292      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10293      * @return {Roo.data.Record[]} An array of Records
10294      */
10295     getRange : function(start, end){
10296         return this.data.getRange(start, end);
10297     },
10298
10299     // private
10300     storeOptions : function(o){
10301         o = Roo.apply({}, o);
10302         delete o.callback;
10303         delete o.scope;
10304         this.lastOptions = o;
10305     },
10306
10307     /**
10308      * Loads the Record cache from the configured Proxy using the configured Reader.
10309      * <p>
10310      * If using remote paging, then the first load call must specify the <em>start</em>
10311      * and <em>limit</em> properties in the options.params property to establish the initial
10312      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10313      * <p>
10314      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10315      * and this call will return before the new data has been loaded. Perform any post-processing
10316      * in a callback function, or in a "load" event handler.</strong>
10317      * <p>
10318      * @param {Object} options An object containing properties which control loading options:<ul>
10319      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10320      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10321      * passed the following arguments:<ul>
10322      * <li>r : Roo.data.Record[]</li>
10323      * <li>options: Options object from the load call</li>
10324      * <li>success: Boolean success indicator</li></ul></li>
10325      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10326      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10327      * </ul>
10328      */
10329     load : function(options){
10330         options = options || {};
10331         if(this.fireEvent("beforeload", this, options) !== false){
10332             this.storeOptions(options);
10333             var p = Roo.apply(options.params || {}, this.baseParams);
10334             // if meta was not loaded from remote source.. try requesting it.
10335             if (!this.reader.metaFromRemote) {
10336                 p._requestMeta = 1;
10337             }
10338             if(this.sortInfo && this.remoteSort){
10339                 var pn = this.paramNames;
10340                 p[pn["sort"]] = this.sortInfo.field;
10341                 p[pn["dir"]] = this.sortInfo.direction;
10342             }
10343             if (this.multiSort) {
10344                 var pn = this.paramNames;
10345                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10346             }
10347             
10348             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10349         }
10350     },
10351
10352     /**
10353      * Reloads the Record cache from the configured Proxy using the configured Reader and
10354      * the options from the last load operation performed.
10355      * @param {Object} options (optional) An object containing properties which may override the options
10356      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10357      * the most recently used options are reused).
10358      */
10359     reload : function(options){
10360         this.load(Roo.applyIf(options||{}, this.lastOptions));
10361     },
10362
10363     // private
10364     // Called as a callback by the Reader during a load operation.
10365     loadRecords : function(o, options, success){
10366         if(!o || success === false){
10367             if(success !== false){
10368                 this.fireEvent("load", this, [], options, o);
10369             }
10370             if(options.callback){
10371                 options.callback.call(options.scope || this, [], options, false);
10372             }
10373             return;
10374         }
10375         // if data returned failure - throw an exception.
10376         if (o.success === false) {
10377             // show a message if no listener is registered.
10378             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10379                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10380             }
10381             // loadmask wil be hooked into this..
10382             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10383             return;
10384         }
10385         var r = o.records, t = o.totalRecords || r.length;
10386         
10387         this.fireEvent("beforeloadadd", this, r, options, o);
10388         
10389         if(!options || options.add !== true){
10390             if(this.pruneModifiedRecords){
10391                 this.modified = [];
10392             }
10393             for(var i = 0, len = r.length; i < len; i++){
10394                 r[i].join(this);
10395             }
10396             if(this.snapshot){
10397                 this.data = this.snapshot;
10398                 delete this.snapshot;
10399             }
10400             this.data.clear();
10401             this.data.addAll(r);
10402             this.totalLength = t;
10403             this.applySort();
10404             this.fireEvent("datachanged", this);
10405         }else{
10406             this.totalLength = Math.max(t, this.data.length+r.length);
10407             this.add(r);
10408         }
10409         this.fireEvent("load", this, r, options, o);
10410         if(options.callback){
10411             options.callback.call(options.scope || this, r, options, true);
10412         }
10413     },
10414
10415
10416     /**
10417      * Loads data from a passed data block. A Reader which understands the format of the data
10418      * must have been configured in the constructor.
10419      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10420      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10421      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10422      */
10423     loadData : function(o, append){
10424         var r = this.reader.readRecords(o);
10425         this.loadRecords(r, {add: append}, true);
10426     },
10427
10428     /**
10429      * Gets the number of cached records.
10430      * <p>
10431      * <em>If using paging, this may not be the total size of the dataset. If the data object
10432      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10433      * the data set size</em>
10434      */
10435     getCount : function(){
10436         return this.data.length || 0;
10437     },
10438
10439     /**
10440      * Gets the total number of records in the dataset as returned by the server.
10441      * <p>
10442      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10443      * the dataset size</em>
10444      */
10445     getTotalCount : function(){
10446         return this.totalLength || 0;
10447     },
10448
10449     /**
10450      * Returns the sort state of the Store as an object with two properties:
10451      * <pre><code>
10452  field {String} The name of the field by which the Records are sorted
10453  direction {String} The sort order, "ASC" or "DESC"
10454      * </code></pre>
10455      */
10456     getSortState : function(){
10457         return this.sortInfo;
10458     },
10459
10460     // private
10461     applySort : function(){
10462         if(this.sortInfo && !this.remoteSort){
10463             var s = this.sortInfo, f = s.field;
10464             var st = this.fields.get(f).sortType;
10465             var fn = function(r1, r2){
10466                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10467                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10468             };
10469             this.data.sort(s.direction, fn);
10470             if(this.snapshot && this.snapshot != this.data){
10471                 this.snapshot.sort(s.direction, fn);
10472             }
10473         }
10474     },
10475
10476     /**
10477      * Sets the default sort column and order to be used by the next load operation.
10478      * @param {String} fieldName The name of the field to sort by.
10479      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10480      */
10481     setDefaultSort : function(field, dir){
10482         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10483     },
10484
10485     /**
10486      * Sort the Records.
10487      * If remote sorting is used, the sort is performed on the server, and the cache is
10488      * reloaded. If local sorting is used, the cache is sorted internally.
10489      * @param {String} fieldName The name of the field to sort by.
10490      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10491      */
10492     sort : function(fieldName, dir){
10493         var f = this.fields.get(fieldName);
10494         if(!dir){
10495             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10496             
10497             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10498                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10499             }else{
10500                 dir = f.sortDir;
10501             }
10502         }
10503         this.sortToggle[f.name] = dir;
10504         this.sortInfo = {field: f.name, direction: dir};
10505         if(!this.remoteSort){
10506             this.applySort();
10507             this.fireEvent("datachanged", this);
10508         }else{
10509             this.load(this.lastOptions);
10510         }
10511     },
10512
10513     /**
10514      * Calls the specified function for each of the Records in the cache.
10515      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10516      * Returning <em>false</em> aborts and exits the iteration.
10517      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10518      */
10519     each : function(fn, scope){
10520         this.data.each(fn, scope);
10521     },
10522
10523     /**
10524      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10525      * (e.g., during paging).
10526      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10527      */
10528     getModifiedRecords : function(){
10529         return this.modified;
10530     },
10531
10532     // private
10533     createFilterFn : function(property, value, anyMatch){
10534         if(!value.exec){ // not a regex
10535             value = String(value);
10536             if(value.length == 0){
10537                 return false;
10538             }
10539             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10540         }
10541         return function(r){
10542             return value.test(r.data[property]);
10543         };
10544     },
10545
10546     /**
10547      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10548      * @param {String} property A field on your records
10549      * @param {Number} start The record index to start at (defaults to 0)
10550      * @param {Number} end The last record index to include (defaults to length - 1)
10551      * @return {Number} The sum
10552      */
10553     sum : function(property, start, end){
10554         var rs = this.data.items, v = 0;
10555         start = start || 0;
10556         end = (end || end === 0) ? end : rs.length-1;
10557
10558         for(var i = start; i <= end; i++){
10559             v += (rs[i].data[property] || 0);
10560         }
10561         return v;
10562     },
10563
10564     /**
10565      * Filter the records by a specified property.
10566      * @param {String} field A field on your records
10567      * @param {String/RegExp} value Either a string that the field
10568      * should start with or a RegExp to test against the field
10569      * @param {Boolean} anyMatch True to match any part not just the beginning
10570      */
10571     filter : function(property, value, anyMatch){
10572         var fn = this.createFilterFn(property, value, anyMatch);
10573         return fn ? this.filterBy(fn) : this.clearFilter();
10574     },
10575
10576     /**
10577      * Filter by a function. The specified function will be called with each
10578      * record in this data source. If the function returns true the record is included,
10579      * otherwise it is filtered.
10580      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10581      * @param {Object} scope (optional) The scope of the function (defaults to this)
10582      */
10583     filterBy : function(fn, scope){
10584         this.snapshot = this.snapshot || this.data;
10585         this.data = this.queryBy(fn, scope||this);
10586         this.fireEvent("datachanged", this);
10587     },
10588
10589     /**
10590      * Query the records by a specified property.
10591      * @param {String} field A field on your records
10592      * @param {String/RegExp} value Either a string that the field
10593      * should start with or a RegExp to test against the field
10594      * @param {Boolean} anyMatch True to match any part not just the beginning
10595      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10596      */
10597     query : function(property, value, anyMatch){
10598         var fn = this.createFilterFn(property, value, anyMatch);
10599         return fn ? this.queryBy(fn) : this.data.clone();
10600     },
10601
10602     /**
10603      * Query by a function. The specified function will be called with each
10604      * record in this data source. If the function returns true the record is included
10605      * in the results.
10606      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10607      * @param {Object} scope (optional) The scope of the function (defaults to this)
10608       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10609      **/
10610     queryBy : function(fn, scope){
10611         var data = this.snapshot || this.data;
10612         return data.filterBy(fn, scope||this);
10613     },
10614
10615     /**
10616      * Collects unique values for a particular dataIndex from this store.
10617      * @param {String} dataIndex The property to collect
10618      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10619      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10620      * @return {Array} An array of the unique values
10621      **/
10622     collect : function(dataIndex, allowNull, bypassFilter){
10623         var d = (bypassFilter === true && this.snapshot) ?
10624                 this.snapshot.items : this.data.items;
10625         var v, sv, r = [], l = {};
10626         for(var i = 0, len = d.length; i < len; i++){
10627             v = d[i].data[dataIndex];
10628             sv = String(v);
10629             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10630                 l[sv] = true;
10631                 r[r.length] = v;
10632             }
10633         }
10634         return r;
10635     },
10636
10637     /**
10638      * Revert to a view of the Record cache with no filtering applied.
10639      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10640      */
10641     clearFilter : function(suppressEvent){
10642         if(this.snapshot && this.snapshot != this.data){
10643             this.data = this.snapshot;
10644             delete this.snapshot;
10645             if(suppressEvent !== true){
10646                 this.fireEvent("datachanged", this);
10647             }
10648         }
10649     },
10650
10651     // private
10652     afterEdit : function(record){
10653         if(this.modified.indexOf(record) == -1){
10654             this.modified.push(record);
10655         }
10656         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10657     },
10658     
10659     // private
10660     afterReject : function(record){
10661         this.modified.remove(record);
10662         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10663     },
10664
10665     // private
10666     afterCommit : function(record){
10667         this.modified.remove(record);
10668         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10669     },
10670
10671     /**
10672      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10673      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10674      */
10675     commitChanges : function(){
10676         var m = this.modified.slice(0);
10677         this.modified = [];
10678         for(var i = 0, len = m.length; i < len; i++){
10679             m[i].commit();
10680         }
10681     },
10682
10683     /**
10684      * Cancel outstanding changes on all changed records.
10685      */
10686     rejectChanges : function(){
10687         var m = this.modified.slice(0);
10688         this.modified = [];
10689         for(var i = 0, len = m.length; i < len; i++){
10690             m[i].reject();
10691         }
10692     },
10693
10694     onMetaChange : function(meta, rtype, o){
10695         this.recordType = rtype;
10696         this.fields = rtype.prototype.fields;
10697         delete this.snapshot;
10698         this.sortInfo = meta.sortInfo || this.sortInfo;
10699         this.modified = [];
10700         this.fireEvent('metachange', this, this.reader.meta);
10701     },
10702     
10703     moveIndex : function(data, type)
10704     {
10705         var index = this.indexOf(data);
10706         
10707         var newIndex = index + type;
10708         
10709         this.remove(data);
10710         
10711         this.insert(newIndex, data);
10712         
10713     }
10714 });/*
10715  * Based on:
10716  * Ext JS Library 1.1.1
10717  * Copyright(c) 2006-2007, Ext JS, LLC.
10718  *
10719  * Originally Released Under LGPL - original licence link has changed is not relivant.
10720  *
10721  * Fork - LGPL
10722  * <script type="text/javascript">
10723  */
10724
10725 /**
10726  * @class Roo.data.SimpleStore
10727  * @extends Roo.data.Store
10728  * Small helper class to make creating Stores from Array data easier.
10729  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10730  * @cfg {Array} fields An array of field definition objects, or field name strings.
10731  * @cfg {Array} data The multi-dimensional array of data
10732  * @constructor
10733  * @param {Object} config
10734  */
10735 Roo.data.SimpleStore = function(config){
10736     Roo.data.SimpleStore.superclass.constructor.call(this, {
10737         isLocal : true,
10738         reader: new Roo.data.ArrayReader({
10739                 id: config.id
10740             },
10741             Roo.data.Record.create(config.fields)
10742         ),
10743         proxy : new Roo.data.MemoryProxy(config.data)
10744     });
10745     this.load();
10746 };
10747 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10748  * Based on:
10749  * Ext JS Library 1.1.1
10750  * Copyright(c) 2006-2007, Ext JS, LLC.
10751  *
10752  * Originally Released Under LGPL - original licence link has changed is not relivant.
10753  *
10754  * Fork - LGPL
10755  * <script type="text/javascript">
10756  */
10757
10758 /**
10759 /**
10760  * @extends Roo.data.Store
10761  * @class Roo.data.JsonStore
10762  * Small helper class to make creating Stores for JSON data easier. <br/>
10763 <pre><code>
10764 var store = new Roo.data.JsonStore({
10765     url: 'get-images.php',
10766     root: 'images',
10767     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10768 });
10769 </code></pre>
10770  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10771  * JsonReader and HttpProxy (unless inline data is provided).</b>
10772  * @cfg {Array} fields An array of field definition objects, or field name strings.
10773  * @constructor
10774  * @param {Object} config
10775  */
10776 Roo.data.JsonStore = function(c){
10777     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10778         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10779         reader: new Roo.data.JsonReader(c, c.fields)
10780     }));
10781 };
10782 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10783  * Based on:
10784  * Ext JS Library 1.1.1
10785  * Copyright(c) 2006-2007, Ext JS, LLC.
10786  *
10787  * Originally Released Under LGPL - original licence link has changed is not relivant.
10788  *
10789  * Fork - LGPL
10790  * <script type="text/javascript">
10791  */
10792
10793  
10794 Roo.data.Field = function(config){
10795     if(typeof config == "string"){
10796         config = {name: config};
10797     }
10798     Roo.apply(this, config);
10799     
10800     if(!this.type){
10801         this.type = "auto";
10802     }
10803     
10804     var st = Roo.data.SortTypes;
10805     // named sortTypes are supported, here we look them up
10806     if(typeof this.sortType == "string"){
10807         this.sortType = st[this.sortType];
10808     }
10809     
10810     // set default sortType for strings and dates
10811     if(!this.sortType){
10812         switch(this.type){
10813             case "string":
10814                 this.sortType = st.asUCString;
10815                 break;
10816             case "date":
10817                 this.sortType = st.asDate;
10818                 break;
10819             default:
10820                 this.sortType = st.none;
10821         }
10822     }
10823
10824     // define once
10825     var stripRe = /[\$,%]/g;
10826
10827     // prebuilt conversion function for this field, instead of
10828     // switching every time we're reading a value
10829     if(!this.convert){
10830         var cv, dateFormat = this.dateFormat;
10831         switch(this.type){
10832             case "":
10833             case "auto":
10834             case undefined:
10835                 cv = function(v){ return v; };
10836                 break;
10837             case "string":
10838                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10839                 break;
10840             case "int":
10841                 cv = function(v){
10842                     return v !== undefined && v !== null && v !== '' ?
10843                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10844                     };
10845                 break;
10846             case "float":
10847                 cv = function(v){
10848                     return v !== undefined && v !== null && v !== '' ?
10849                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10850                     };
10851                 break;
10852             case "bool":
10853             case "boolean":
10854                 cv = function(v){ return v === true || v === "true" || v == 1; };
10855                 break;
10856             case "date":
10857                 cv = function(v){
10858                     if(!v){
10859                         return '';
10860                     }
10861                     if(v instanceof Date){
10862                         return v;
10863                     }
10864                     if(dateFormat){
10865                         if(dateFormat == "timestamp"){
10866                             return new Date(v*1000);
10867                         }
10868                         return Date.parseDate(v, dateFormat);
10869                     }
10870                     var parsed = Date.parse(v);
10871                     return parsed ? new Date(parsed) : null;
10872                 };
10873              break;
10874             
10875         }
10876         this.convert = cv;
10877     }
10878 };
10879
10880 Roo.data.Field.prototype = {
10881     dateFormat: null,
10882     defaultValue: "",
10883     mapping: null,
10884     sortType : null,
10885     sortDir : "ASC"
10886 };/*
10887  * Based on:
10888  * Ext JS Library 1.1.1
10889  * Copyright(c) 2006-2007, Ext JS, LLC.
10890  *
10891  * Originally Released Under LGPL - original licence link has changed is not relivant.
10892  *
10893  * Fork - LGPL
10894  * <script type="text/javascript">
10895  */
10896  
10897 // Base class for reading structured data from a data source.  This class is intended to be
10898 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10899
10900 /**
10901  * @class Roo.data.DataReader
10902  * Base class for reading structured data from a data source.  This class is intended to be
10903  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10904  */
10905
10906 Roo.data.DataReader = function(meta, recordType){
10907     
10908     this.meta = meta;
10909     
10910     this.recordType = recordType instanceof Array ? 
10911         Roo.data.Record.create(recordType) : recordType;
10912 };
10913
10914 Roo.data.DataReader.prototype = {
10915      /**
10916      * Create an empty record
10917      * @param {Object} data (optional) - overlay some values
10918      * @return {Roo.data.Record} record created.
10919      */
10920     newRow :  function(d) {
10921         var da =  {};
10922         this.recordType.prototype.fields.each(function(c) {
10923             switch( c.type) {
10924                 case 'int' : da[c.name] = 0; break;
10925                 case 'date' : da[c.name] = new Date(); break;
10926                 case 'float' : da[c.name] = 0.0; break;
10927                 case 'boolean' : da[c.name] = false; break;
10928                 default : da[c.name] = ""; break;
10929             }
10930             
10931         });
10932         return new this.recordType(Roo.apply(da, d));
10933     }
10934     
10935 };/*
10936  * Based on:
10937  * Ext JS Library 1.1.1
10938  * Copyright(c) 2006-2007, Ext JS, LLC.
10939  *
10940  * Originally Released Under LGPL - original licence link has changed is not relivant.
10941  *
10942  * Fork - LGPL
10943  * <script type="text/javascript">
10944  */
10945
10946 /**
10947  * @class Roo.data.DataProxy
10948  * @extends Roo.data.Observable
10949  * This class is an abstract base class for implementations which provide retrieval of
10950  * unformatted data objects.<br>
10951  * <p>
10952  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10953  * (of the appropriate type which knows how to parse the data object) to provide a block of
10954  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10955  * <p>
10956  * Custom implementations must implement the load method as described in
10957  * {@link Roo.data.HttpProxy#load}.
10958  */
10959 Roo.data.DataProxy = function(){
10960     this.addEvents({
10961         /**
10962          * @event beforeload
10963          * Fires before a network request is made to retrieve a data object.
10964          * @param {Object} This DataProxy object.
10965          * @param {Object} params The params parameter to the load function.
10966          */
10967         beforeload : true,
10968         /**
10969          * @event load
10970          * Fires before the load method's callback is called.
10971          * @param {Object} This DataProxy object.
10972          * @param {Object} o The data object.
10973          * @param {Object} arg The callback argument object passed to the load function.
10974          */
10975         load : true,
10976         /**
10977          * @event loadexception
10978          * Fires if an Exception occurs during data retrieval.
10979          * @param {Object} This DataProxy object.
10980          * @param {Object} o The data object.
10981          * @param {Object} arg The callback argument object passed to the load function.
10982          * @param {Object} e The Exception.
10983          */
10984         loadexception : true
10985     });
10986     Roo.data.DataProxy.superclass.constructor.call(this);
10987 };
10988
10989 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10990
10991     /**
10992      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10993      */
10994 /*
10995  * Based on:
10996  * Ext JS Library 1.1.1
10997  * Copyright(c) 2006-2007, Ext JS, LLC.
10998  *
10999  * Originally Released Under LGPL - original licence link has changed is not relivant.
11000  *
11001  * Fork - LGPL
11002  * <script type="text/javascript">
11003  */
11004 /**
11005  * @class Roo.data.MemoryProxy
11006  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11007  * to the Reader when its load method is called.
11008  * @constructor
11009  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11010  */
11011 Roo.data.MemoryProxy = function(data){
11012     if (data.data) {
11013         data = data.data;
11014     }
11015     Roo.data.MemoryProxy.superclass.constructor.call(this);
11016     this.data = data;
11017 };
11018
11019 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11020     
11021     /**
11022      * Load data from the requested source (in this case an in-memory
11023      * data object passed to the constructor), read the data object into
11024      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11025      * process that block using the passed callback.
11026      * @param {Object} params This parameter is not used by the MemoryProxy class.
11027      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11028      * object into a block of Roo.data.Records.
11029      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11030      * The function must be passed <ul>
11031      * <li>The Record block object</li>
11032      * <li>The "arg" argument from the load function</li>
11033      * <li>A boolean success indicator</li>
11034      * </ul>
11035      * @param {Object} scope The scope in which to call the callback
11036      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11037      */
11038     load : function(params, reader, callback, scope, arg){
11039         params = params || {};
11040         var result;
11041         try {
11042             result = reader.readRecords(this.data);
11043         }catch(e){
11044             this.fireEvent("loadexception", this, arg, null, e);
11045             callback.call(scope, null, arg, false);
11046             return;
11047         }
11048         callback.call(scope, result, arg, true);
11049     },
11050     
11051     // private
11052     update : function(params, records){
11053         
11054     }
11055 });/*
11056  * Based on:
11057  * Ext JS Library 1.1.1
11058  * Copyright(c) 2006-2007, Ext JS, LLC.
11059  *
11060  * Originally Released Under LGPL - original licence link has changed is not relivant.
11061  *
11062  * Fork - LGPL
11063  * <script type="text/javascript">
11064  */
11065 /**
11066  * @class Roo.data.HttpProxy
11067  * @extends Roo.data.DataProxy
11068  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11069  * configured to reference a certain URL.<br><br>
11070  * <p>
11071  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11072  * from which the running page was served.<br><br>
11073  * <p>
11074  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11075  * <p>
11076  * Be aware that to enable the browser to parse an XML document, the server must set
11077  * the Content-Type header in the HTTP response to "text/xml".
11078  * @constructor
11079  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11080  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11081  * will be used to make the request.
11082  */
11083 Roo.data.HttpProxy = function(conn){
11084     Roo.data.HttpProxy.superclass.constructor.call(this);
11085     // is conn a conn config or a real conn?
11086     this.conn = conn;
11087     this.useAjax = !conn || !conn.events;
11088   
11089 };
11090
11091 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11092     // thse are take from connection...
11093     
11094     /**
11095      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11096      */
11097     /**
11098      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11099      * extra parameters to each request made by this object. (defaults to undefined)
11100      */
11101     /**
11102      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11103      *  to each request made by this object. (defaults to undefined)
11104      */
11105     /**
11106      * @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)
11107      */
11108     /**
11109      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11110      */
11111      /**
11112      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11113      * @type Boolean
11114      */
11115   
11116
11117     /**
11118      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11119      * @type Boolean
11120      */
11121     /**
11122      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11123      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11124      * a finer-grained basis than the DataProxy events.
11125      */
11126     getConnection : function(){
11127         return this.useAjax ? Roo.Ajax : this.conn;
11128     },
11129
11130     /**
11131      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11132      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11133      * process that block using the passed callback.
11134      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11135      * for the request to the remote server.
11136      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11137      * object into a block of Roo.data.Records.
11138      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11139      * The function must be passed <ul>
11140      * <li>The Record block object</li>
11141      * <li>The "arg" argument from the load function</li>
11142      * <li>A boolean success indicator</li>
11143      * </ul>
11144      * @param {Object} scope The scope in which to call the callback
11145      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11146      */
11147     load : function(params, reader, callback, scope, arg){
11148         if(this.fireEvent("beforeload", this, params) !== false){
11149             var  o = {
11150                 params : params || {},
11151                 request: {
11152                     callback : callback,
11153                     scope : scope,
11154                     arg : arg
11155                 },
11156                 reader: reader,
11157                 callback : this.loadResponse,
11158                 scope: this
11159             };
11160             if(this.useAjax){
11161                 Roo.applyIf(o, this.conn);
11162                 if(this.activeRequest){
11163                     Roo.Ajax.abort(this.activeRequest);
11164                 }
11165                 this.activeRequest = Roo.Ajax.request(o);
11166             }else{
11167                 this.conn.request(o);
11168             }
11169         }else{
11170             callback.call(scope||this, null, arg, false);
11171         }
11172     },
11173
11174     // private
11175     loadResponse : function(o, success, response){
11176         delete this.activeRequest;
11177         if(!success){
11178             this.fireEvent("loadexception", this, o, response);
11179             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11180             return;
11181         }
11182         var result;
11183         try {
11184             result = o.reader.read(response);
11185         }catch(e){
11186             this.fireEvent("loadexception", this, o, response, e);
11187             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11188             return;
11189         }
11190         
11191         this.fireEvent("load", this, o, o.request.arg);
11192         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11193     },
11194
11195     // private
11196     update : function(dataSet){
11197
11198     },
11199
11200     // private
11201     updateResponse : function(dataSet){
11202
11203     }
11204 });/*
11205  * Based on:
11206  * Ext JS Library 1.1.1
11207  * Copyright(c) 2006-2007, Ext JS, LLC.
11208  *
11209  * Originally Released Under LGPL - original licence link has changed is not relivant.
11210  *
11211  * Fork - LGPL
11212  * <script type="text/javascript">
11213  */
11214
11215 /**
11216  * @class Roo.data.ScriptTagProxy
11217  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11218  * other than the originating domain of the running page.<br><br>
11219  * <p>
11220  * <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
11221  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11222  * <p>
11223  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11224  * source code that is used as the source inside a &lt;script> tag.<br><br>
11225  * <p>
11226  * In order for the browser to process the returned data, the server must wrap the data object
11227  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11228  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11229  * depending on whether the callback name was passed:
11230  * <p>
11231  * <pre><code>
11232 boolean scriptTag = false;
11233 String cb = request.getParameter("callback");
11234 if (cb != null) {
11235     scriptTag = true;
11236     response.setContentType("text/javascript");
11237 } else {
11238     response.setContentType("application/x-json");
11239 }
11240 Writer out = response.getWriter();
11241 if (scriptTag) {
11242     out.write(cb + "(");
11243 }
11244 out.print(dataBlock.toJsonString());
11245 if (scriptTag) {
11246     out.write(");");
11247 }
11248 </pre></code>
11249  *
11250  * @constructor
11251  * @param {Object} config A configuration object.
11252  */
11253 Roo.data.ScriptTagProxy = function(config){
11254     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11255     Roo.apply(this, config);
11256     this.head = document.getElementsByTagName("head")[0];
11257 };
11258
11259 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11260
11261 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11262     /**
11263      * @cfg {String} url The URL from which to request the data object.
11264      */
11265     /**
11266      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11267      */
11268     timeout : 30000,
11269     /**
11270      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11271      * the server the name of the callback function set up by the load call to process the returned data object.
11272      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11273      * javascript output which calls this named function passing the data object as its only parameter.
11274      */
11275     callbackParam : "callback",
11276     /**
11277      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11278      * name to the request.
11279      */
11280     nocache : true,
11281
11282     /**
11283      * Load data from the configured URL, read the data object into
11284      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11285      * process that block using the passed callback.
11286      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11287      * for the request to the remote server.
11288      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11289      * object into a block of Roo.data.Records.
11290      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11291      * The function must be passed <ul>
11292      * <li>The Record block object</li>
11293      * <li>The "arg" argument from the load function</li>
11294      * <li>A boolean success indicator</li>
11295      * </ul>
11296      * @param {Object} scope The scope in which to call the callback
11297      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11298      */
11299     load : function(params, reader, callback, scope, arg){
11300         if(this.fireEvent("beforeload", this, params) !== false){
11301
11302             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11303
11304             var url = this.url;
11305             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11306             if(this.nocache){
11307                 url += "&_dc=" + (new Date().getTime());
11308             }
11309             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11310             var trans = {
11311                 id : transId,
11312                 cb : "stcCallback"+transId,
11313                 scriptId : "stcScript"+transId,
11314                 params : params,
11315                 arg : arg,
11316                 url : url,
11317                 callback : callback,
11318                 scope : scope,
11319                 reader : reader
11320             };
11321             var conn = this;
11322
11323             window[trans.cb] = function(o){
11324                 conn.handleResponse(o, trans);
11325             };
11326
11327             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11328
11329             if(this.autoAbort !== false){
11330                 this.abort();
11331             }
11332
11333             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11334
11335             var script = document.createElement("script");
11336             script.setAttribute("src", url);
11337             script.setAttribute("type", "text/javascript");
11338             script.setAttribute("id", trans.scriptId);
11339             this.head.appendChild(script);
11340
11341             this.trans = trans;
11342         }else{
11343             callback.call(scope||this, null, arg, false);
11344         }
11345     },
11346
11347     // private
11348     isLoading : function(){
11349         return this.trans ? true : false;
11350     },
11351
11352     /**
11353      * Abort the current server request.
11354      */
11355     abort : function(){
11356         if(this.isLoading()){
11357             this.destroyTrans(this.trans);
11358         }
11359     },
11360
11361     // private
11362     destroyTrans : function(trans, isLoaded){
11363         this.head.removeChild(document.getElementById(trans.scriptId));
11364         clearTimeout(trans.timeoutId);
11365         if(isLoaded){
11366             window[trans.cb] = undefined;
11367             try{
11368                 delete window[trans.cb];
11369             }catch(e){}
11370         }else{
11371             // if hasn't been loaded, wait for load to remove it to prevent script error
11372             window[trans.cb] = function(){
11373                 window[trans.cb] = undefined;
11374                 try{
11375                     delete window[trans.cb];
11376                 }catch(e){}
11377             };
11378         }
11379     },
11380
11381     // private
11382     handleResponse : function(o, trans){
11383         this.trans = false;
11384         this.destroyTrans(trans, true);
11385         var result;
11386         try {
11387             result = trans.reader.readRecords(o);
11388         }catch(e){
11389             this.fireEvent("loadexception", this, o, trans.arg, e);
11390             trans.callback.call(trans.scope||window, null, trans.arg, false);
11391             return;
11392         }
11393         this.fireEvent("load", this, o, trans.arg);
11394         trans.callback.call(trans.scope||window, result, trans.arg, true);
11395     },
11396
11397     // private
11398     handleFailure : function(trans){
11399         this.trans = false;
11400         this.destroyTrans(trans, false);
11401         this.fireEvent("loadexception", this, null, trans.arg);
11402         trans.callback.call(trans.scope||window, null, trans.arg, false);
11403     }
11404 });/*
11405  * Based on:
11406  * Ext JS Library 1.1.1
11407  * Copyright(c) 2006-2007, Ext JS, LLC.
11408  *
11409  * Originally Released Under LGPL - original licence link has changed is not relivant.
11410  *
11411  * Fork - LGPL
11412  * <script type="text/javascript">
11413  */
11414
11415 /**
11416  * @class Roo.data.JsonReader
11417  * @extends Roo.data.DataReader
11418  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11419  * based on mappings in a provided Roo.data.Record constructor.
11420  * 
11421  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11422  * in the reply previously. 
11423  * 
11424  * <p>
11425  * Example code:
11426  * <pre><code>
11427 var RecordDef = Roo.data.Record.create([
11428     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11429     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11430 ]);
11431 var myReader = new Roo.data.JsonReader({
11432     totalProperty: "results",    // The property which contains the total dataset size (optional)
11433     root: "rows",                // The property which contains an Array of row objects
11434     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11435 }, RecordDef);
11436 </code></pre>
11437  * <p>
11438  * This would consume a JSON file like this:
11439  * <pre><code>
11440 { 'results': 2, 'rows': [
11441     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11442     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11443 }
11444 </code></pre>
11445  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11446  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11447  * paged from the remote server.
11448  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11449  * @cfg {String} root name of the property which contains the Array of row objects.
11450  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11451  * @cfg {Array} fields Array of field definition objects
11452  * @constructor
11453  * Create a new JsonReader
11454  * @param {Object} meta Metadata configuration options
11455  * @param {Object} recordType Either an Array of field definition objects,
11456  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11457  */
11458 Roo.data.JsonReader = function(meta, recordType){
11459     
11460     meta = meta || {};
11461     // set some defaults:
11462     Roo.applyIf(meta, {
11463         totalProperty: 'total',
11464         successProperty : 'success',
11465         root : 'data',
11466         id : 'id'
11467     });
11468     
11469     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11470 };
11471 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11472     
11473     /**
11474      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11475      * Used by Store query builder to append _requestMeta to params.
11476      * 
11477      */
11478     metaFromRemote : false,
11479     /**
11480      * This method is only used by a DataProxy which has retrieved data from a remote server.
11481      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11482      * @return {Object} data A data block which is used by an Roo.data.Store object as
11483      * a cache of Roo.data.Records.
11484      */
11485     read : function(response){
11486         var json = response.responseText;
11487        
11488         var o = /* eval:var:o */ eval("("+json+")");
11489         if(!o) {
11490             throw {message: "JsonReader.read: Json object not found"};
11491         }
11492         
11493         if(o.metaData){
11494             
11495             delete this.ef;
11496             this.metaFromRemote = true;
11497             this.meta = o.metaData;
11498             this.recordType = Roo.data.Record.create(o.metaData.fields);
11499             this.onMetaChange(this.meta, this.recordType, o);
11500         }
11501         return this.readRecords(o);
11502     },
11503
11504     // private function a store will implement
11505     onMetaChange : function(meta, recordType, o){
11506
11507     },
11508
11509     /**
11510          * @ignore
11511          */
11512     simpleAccess: function(obj, subsc) {
11513         return obj[subsc];
11514     },
11515
11516         /**
11517          * @ignore
11518          */
11519     getJsonAccessor: function(){
11520         var re = /[\[\.]/;
11521         return function(expr) {
11522             try {
11523                 return(re.test(expr))
11524                     ? new Function("obj", "return obj." + expr)
11525                     : function(obj){
11526                         return obj[expr];
11527                     };
11528             } catch(e){}
11529             return Roo.emptyFn;
11530         };
11531     }(),
11532
11533     /**
11534      * Create a data block containing Roo.data.Records from an XML document.
11535      * @param {Object} o An object which contains an Array of row objects in the property specified
11536      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11537      * which contains the total size of the dataset.
11538      * @return {Object} data A data block which is used by an Roo.data.Store object as
11539      * a cache of Roo.data.Records.
11540      */
11541     readRecords : function(o){
11542         /**
11543          * After any data loads, the raw JSON data is available for further custom processing.
11544          * @type Object
11545          */
11546         this.o = o;
11547         var s = this.meta, Record = this.recordType,
11548             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11549
11550 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11551         if (!this.ef) {
11552             if(s.totalProperty) {
11553                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11554                 }
11555                 if(s.successProperty) {
11556                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11557                 }
11558                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11559                 if (s.id) {
11560                         var g = this.getJsonAccessor(s.id);
11561                         this.getId = function(rec) {
11562                                 var r = g(rec);  
11563                                 return (r === undefined || r === "") ? null : r;
11564                         };
11565                 } else {
11566                         this.getId = function(){return null;};
11567                 }
11568             this.ef = [];
11569             for(var jj = 0; jj < fl; jj++){
11570                 f = fi[jj];
11571                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11572                 this.ef[jj] = this.getJsonAccessor(map);
11573             }
11574         }
11575
11576         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11577         if(s.totalProperty){
11578             var vt = parseInt(this.getTotal(o), 10);
11579             if(!isNaN(vt)){
11580                 totalRecords = vt;
11581             }
11582         }
11583         if(s.successProperty){
11584             var vs = this.getSuccess(o);
11585             if(vs === false || vs === 'false'){
11586                 success = false;
11587             }
11588         }
11589         var records = [];
11590         for(var i = 0; i < c; i++){
11591                 var n = root[i];
11592             var values = {};
11593             var id = this.getId(n);
11594             for(var j = 0; j < fl; j++){
11595                 f = fi[j];
11596             var v = this.ef[j](n);
11597             if (!f.convert) {
11598                 Roo.log('missing convert for ' + f.name);
11599                 Roo.log(f);
11600                 continue;
11601             }
11602             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11603             }
11604             var record = new Record(values, id);
11605             record.json = n;
11606             records[i] = record;
11607         }
11608         return {
11609             raw : o,
11610             success : success,
11611             records : records,
11612             totalRecords : totalRecords
11613         };
11614     }
11615 });/*
11616  * Based on:
11617  * Ext JS Library 1.1.1
11618  * Copyright(c) 2006-2007, Ext JS, LLC.
11619  *
11620  * Originally Released Under LGPL - original licence link has changed is not relivant.
11621  *
11622  * Fork - LGPL
11623  * <script type="text/javascript">
11624  */
11625
11626 /**
11627  * @class Roo.data.ArrayReader
11628  * @extends Roo.data.DataReader
11629  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11630  * Each element of that Array represents a row of data fields. The
11631  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11632  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11633  * <p>
11634  * Example code:.
11635  * <pre><code>
11636 var RecordDef = Roo.data.Record.create([
11637     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11638     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11639 ]);
11640 var myReader = new Roo.data.ArrayReader({
11641     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11642 }, RecordDef);
11643 </code></pre>
11644  * <p>
11645  * This would consume an Array like this:
11646  * <pre><code>
11647 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11648   </code></pre>
11649  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11650  * @constructor
11651  * Create a new JsonReader
11652  * @param {Object} meta Metadata configuration options.
11653  * @param {Object} recordType Either an Array of field definition objects
11654  * as specified to {@link Roo.data.Record#create},
11655  * or an {@link Roo.data.Record} object
11656  * created using {@link Roo.data.Record#create}.
11657  */
11658 Roo.data.ArrayReader = function(meta, recordType){
11659     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11660 };
11661
11662 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11663     /**
11664      * Create a data block containing Roo.data.Records from an XML document.
11665      * @param {Object} o An Array of row objects which represents the dataset.
11666      * @return {Object} data A data block which is used by an Roo.data.Store object as
11667      * a cache of Roo.data.Records.
11668      */
11669     readRecords : function(o){
11670         var sid = this.meta ? this.meta.id : null;
11671         var recordType = this.recordType, fields = recordType.prototype.fields;
11672         var records = [];
11673         var root = o;
11674             for(var i = 0; i < root.length; i++){
11675                     var n = root[i];
11676                 var values = {};
11677                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11678                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11679                 var f = fields.items[j];
11680                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11681                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11682                 v = f.convert(v);
11683                 values[f.name] = v;
11684             }
11685                 var record = new recordType(values, id);
11686                 record.json = n;
11687                 records[records.length] = record;
11688             }
11689             return {
11690                 records : records,
11691                 totalRecords : records.length
11692             };
11693     }
11694 });/*
11695  * - LGPL
11696  * * 
11697  */
11698
11699 /**
11700  * @class Roo.bootstrap.ComboBox
11701  * @extends Roo.bootstrap.TriggerField
11702  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11703  * @cfg {Boolean} append (true|false) default false
11704  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11705  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11706  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11707  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11708  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11709  * @cfg {Boolean} animate default true
11710  * @cfg {Boolean} emptyResultText only for touch device
11711  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11712  * @constructor
11713  * Create a new ComboBox.
11714  * @param {Object} config Configuration options
11715  */
11716 Roo.bootstrap.ComboBox = function(config){
11717     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11718     this.addEvents({
11719         /**
11720          * @event expand
11721          * Fires when the dropdown list is expanded
11722              * @param {Roo.bootstrap.ComboBox} combo This combo box
11723              */
11724         'expand' : true,
11725         /**
11726          * @event collapse
11727          * Fires when the dropdown list is collapsed
11728              * @param {Roo.bootstrap.ComboBox} combo This combo box
11729              */
11730         'collapse' : true,
11731         /**
11732          * @event beforeselect
11733          * Fires before a list item is selected. Return false to cancel the selection.
11734              * @param {Roo.bootstrap.ComboBox} combo This combo box
11735              * @param {Roo.data.Record} record The data record returned from the underlying store
11736              * @param {Number} index The index of the selected item in the dropdown list
11737              */
11738         'beforeselect' : true,
11739         /**
11740          * @event select
11741          * Fires when a list item is selected
11742              * @param {Roo.bootstrap.ComboBox} combo This combo box
11743              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11744              * @param {Number} index The index of the selected item in the dropdown list
11745              */
11746         'select' : true,
11747         /**
11748          * @event beforequery
11749          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11750          * The event object passed has these properties:
11751              * @param {Roo.bootstrap.ComboBox} combo This combo box
11752              * @param {String} query The query
11753              * @param {Boolean} forceAll true to force "all" query
11754              * @param {Boolean} cancel true to cancel the query
11755              * @param {Object} e The query event object
11756              */
11757         'beforequery': true,
11758          /**
11759          * @event add
11760          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11761              * @param {Roo.bootstrap.ComboBox} combo This combo box
11762              */
11763         'add' : true,
11764         /**
11765          * @event edit
11766          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11767              * @param {Roo.bootstrap.ComboBox} combo This combo box
11768              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11769              */
11770         'edit' : true,
11771         /**
11772          * @event remove
11773          * Fires when the remove value from the combobox array
11774              * @param {Roo.bootstrap.ComboBox} combo This combo box
11775              */
11776         'remove' : true,
11777         /**
11778          * @event afterremove
11779          * Fires when the remove value from the combobox array
11780              * @param {Roo.bootstrap.ComboBox} combo This combo box
11781              */
11782         'afterremove' : true,
11783         /**
11784          * @event specialfilter
11785          * Fires when specialfilter
11786             * @param {Roo.bootstrap.ComboBox} combo This combo box
11787             */
11788         'specialfilter' : true,
11789         /**
11790          * @event tick
11791          * Fires when tick the element
11792             * @param {Roo.bootstrap.ComboBox} combo This combo box
11793             */
11794         'tick' : true,
11795         /**
11796          * @event touchviewdisplay
11797          * Fires when touch view require special display (default is using displayField)
11798             * @param {Roo.bootstrap.ComboBox} combo This combo box
11799             * @param {Object} cfg set html .
11800             */
11801         'touchviewdisplay' : true
11802         
11803     });
11804     
11805     this.item = [];
11806     this.tickItems = [];
11807     
11808     this.selectedIndex = -1;
11809     if(this.mode == 'local'){
11810         if(config.queryDelay === undefined){
11811             this.queryDelay = 10;
11812         }
11813         if(config.minChars === undefined){
11814             this.minChars = 0;
11815         }
11816     }
11817 };
11818
11819 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11820      
11821     /**
11822      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11823      * rendering into an Roo.Editor, defaults to false)
11824      */
11825     /**
11826      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11827      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11828      */
11829     /**
11830      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11831      */
11832     /**
11833      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11834      * the dropdown list (defaults to undefined, with no header element)
11835      */
11836
11837      /**
11838      * @cfg {String/Roo.Template} tpl The template to use to render the output
11839      */
11840      
11841      /**
11842      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11843      */
11844     listWidth: undefined,
11845     /**
11846      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11847      * mode = 'remote' or 'text' if mode = 'local')
11848      */
11849     displayField: undefined,
11850     
11851     /**
11852      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11853      * mode = 'remote' or 'value' if mode = 'local'). 
11854      * Note: use of a valueField requires the user make a selection
11855      * in order for a value to be mapped.
11856      */
11857     valueField: undefined,
11858     
11859     
11860     /**
11861      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11862      * field's data value (defaults to the underlying DOM element's name)
11863      */
11864     hiddenName: undefined,
11865     /**
11866      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11867      */
11868     listClass: '',
11869     /**
11870      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11871      */
11872     selectedClass: 'active',
11873     
11874     /**
11875      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11876      */
11877     shadow:'sides',
11878     /**
11879      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11880      * anchor positions (defaults to 'tl-bl')
11881      */
11882     listAlign: 'tl-bl?',
11883     /**
11884      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11885      */
11886     maxHeight: 300,
11887     /**
11888      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11889      * query specified by the allQuery config option (defaults to 'query')
11890      */
11891     triggerAction: 'query',
11892     /**
11893      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11894      * (defaults to 4, does not apply if editable = false)
11895      */
11896     minChars : 4,
11897     /**
11898      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11899      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11900      */
11901     typeAhead: false,
11902     /**
11903      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11904      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11905      */
11906     queryDelay: 500,
11907     /**
11908      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11909      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11910      */
11911     pageSize: 0,
11912     /**
11913      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11914      * when editable = true (defaults to false)
11915      */
11916     selectOnFocus:false,
11917     /**
11918      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11919      */
11920     queryParam: 'query',
11921     /**
11922      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11923      * when mode = 'remote' (defaults to 'Loading...')
11924      */
11925     loadingText: 'Loading...',
11926     /**
11927      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11928      */
11929     resizable: false,
11930     /**
11931      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11932      */
11933     handleHeight : 8,
11934     /**
11935      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11936      * traditional select (defaults to true)
11937      */
11938     editable: true,
11939     /**
11940      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11941      */
11942     allQuery: '',
11943     /**
11944      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11945      */
11946     mode: 'remote',
11947     /**
11948      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11949      * listWidth has a higher value)
11950      */
11951     minListWidth : 70,
11952     /**
11953      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11954      * allow the user to set arbitrary text into the field (defaults to false)
11955      */
11956     forceSelection:false,
11957     /**
11958      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11959      * if typeAhead = true (defaults to 250)
11960      */
11961     typeAheadDelay : 250,
11962     /**
11963      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11964      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11965      */
11966     valueNotFoundText : undefined,
11967     /**
11968      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11969      */
11970     blockFocus : false,
11971     
11972     /**
11973      * @cfg {Boolean} disableClear Disable showing of clear button.
11974      */
11975     disableClear : false,
11976     /**
11977      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11978      */
11979     alwaysQuery : false,
11980     
11981     /**
11982      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11983      */
11984     multiple : false,
11985     
11986     /**
11987      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11988      */
11989     invalidClass : "has-warning",
11990     
11991     /**
11992      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11993      */
11994     validClass : "has-success",
11995     
11996     /**
11997      * @cfg {Boolean} specialFilter (true|false) special filter default false
11998      */
11999     specialFilter : false,
12000     
12001     /**
12002      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12003      */
12004     mobileTouchView : true,
12005     
12006     //private
12007     addicon : false,
12008     editicon: false,
12009     
12010     page: 0,
12011     hasQuery: false,
12012     append: false,
12013     loadNext: false,
12014     autoFocus : true,
12015     tickable : false,
12016     btnPosition : 'right',
12017     triggerList : true,
12018     showToggleBtn : true,
12019     animate : true,
12020     emptyResultText: 'Empty',
12021     triggerText : 'Select',
12022     
12023     // element that contains real text value.. (when hidden is used..)
12024     
12025     getAutoCreate : function()
12026     {
12027         var cfg = false;
12028         
12029         /*
12030          * Touch Devices
12031          */
12032         
12033         if(Roo.isTouch && this.mobileTouchView){
12034             cfg = this.getAutoCreateTouchView();
12035             return cfg;;
12036         }
12037         
12038         /*
12039          *  Normal ComboBox
12040          */
12041         if(!this.tickable){
12042             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12043             return cfg;
12044         }
12045         
12046         /*
12047          *  ComboBox with tickable selections
12048          */
12049              
12050         var align = this.labelAlign || this.parentLabelAlign();
12051         
12052         cfg = {
12053             cls : 'form-group roo-combobox-tickable' //input-group
12054         };
12055         
12056         var buttons = {
12057             tag : 'div',
12058             cls : 'tickable-buttons',
12059             cn : [
12060                 {
12061                     tag : 'button',
12062                     type : 'button',
12063                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12064                     html : this.triggerText
12065                 },
12066                 {
12067                     tag : 'button',
12068                     type : 'button',
12069                     name : 'ok',
12070                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12071                     html : 'Done'
12072                 },
12073                 {
12074                     tag : 'button',
12075                     type : 'button',
12076                     name : 'cancel',
12077                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12078                     html : 'Cancel'
12079                 }
12080             ]
12081         };
12082         
12083         if(this.editable){
12084             buttons.cn.unshift({
12085                 tag: 'input',
12086                 cls: 'roo-select2-search-field-input'
12087             });
12088         }
12089         
12090         var _this = this;
12091         
12092         Roo.each(buttons.cn, function(c){
12093             if (_this.size) {
12094                 c.cls += ' btn-' + _this.size;
12095             }
12096
12097             if (_this.disabled) {
12098                 c.disabled = true;
12099             }
12100         });
12101         
12102         var box = {
12103             tag: 'div',
12104             cn: [
12105                 {
12106                     tag: 'input',
12107                     type : 'hidden',
12108                     cls: 'form-hidden-field'
12109                 },
12110                 {
12111                     tag: 'ul',
12112                     cls: 'roo-select2-choices',
12113                     cn:[
12114                         {
12115                             tag: 'li',
12116                             cls: 'roo-select2-search-field',
12117                             cn: [
12118
12119                                 buttons
12120                             ]
12121                         }
12122                     ]
12123                 }
12124             ]
12125         };
12126         
12127         var combobox = {
12128             cls: 'roo-select2-container input-group roo-select2-container-multi',
12129             cn: [
12130                 box
12131 //                {
12132 //                    tag: 'ul',
12133 //                    cls: 'typeahead typeahead-long dropdown-menu',
12134 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12135 //                }
12136             ]
12137         };
12138         
12139         if(this.hasFeedback && !this.allowBlank){
12140             
12141             var feedback = {
12142                 tag: 'span',
12143                 cls: 'glyphicon form-control-feedback'
12144             };
12145
12146             combobox.cn.push(feedback);
12147         }
12148         
12149         if (align ==='left' && this.fieldLabel.length) {
12150             
12151 //                Roo.log("left and has label");
12152                 cfg.cn = [
12153                     
12154                     {
12155                         tag: 'label',
12156                         'for' :  id,
12157                         cls : 'control-label col-sm-' + this.labelWidth,
12158                         html : this.fieldLabel
12159                         
12160                     },
12161                     {
12162                         cls : "col-sm-" + (12 - this.labelWidth), 
12163                         cn: [
12164                             combobox
12165                         ]
12166                     }
12167                     
12168                 ];
12169         } else if ( this.fieldLabel.length) {
12170 //                Roo.log(" label");
12171                  cfg.cn = [
12172                    
12173                     {
12174                         tag: 'label',
12175                         //cls : 'input-group-addon',
12176                         html : this.fieldLabel
12177                         
12178                     },
12179                     
12180                     combobox
12181                     
12182                 ];
12183
12184         } else {
12185             
12186 //                Roo.log(" no label && no align");
12187                 cfg = combobox
12188                      
12189                 
12190         }
12191          
12192         var settings=this;
12193         ['xs','sm','md','lg'].map(function(size){
12194             if (settings[size]) {
12195                 cfg.cls += ' col-' + size + '-' + settings[size];
12196             }
12197         });
12198         
12199         return cfg;
12200         
12201     },
12202     
12203     _initEventsCalled : false,
12204     
12205     // private
12206     initEvents: function()
12207     {
12208         
12209         if (this._initEventsCalled) { // as we call render... prevent looping...
12210             return;
12211         }
12212         this._initEventsCalled = true;
12213         
12214         if (!this.store) {
12215             throw "can not find store for combo";
12216         }
12217         
12218         this.store = Roo.factory(this.store, Roo.data);
12219         
12220         // if we are building from html. then this element is so complex, that we can not really
12221         // use the rendered HTML.
12222         // so we have to trash and replace the previous code.
12223         if (Roo.XComponent.build_from_html) {
12224             
12225             // remove this element....
12226             var e = this.el.dom, k=0;
12227             while (e ) { e = e.previousSibling;  ++k;}
12228
12229             this.el.remove();
12230             
12231             this.el=false;
12232             this.rendered = false;
12233             
12234             this.render(this.parent().getChildContainer(true), k);
12235             
12236             
12237             
12238         }
12239         
12240         
12241         /*
12242          * Touch Devices
12243          */
12244         
12245         if(Roo.isTouch && this.mobileTouchView){
12246             this.initTouchView();
12247             return;
12248         }
12249         
12250         if(this.tickable){
12251             this.initTickableEvents();
12252             return;
12253         }
12254         
12255         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12256         
12257         if(this.hiddenName){
12258             
12259             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12260             
12261             this.hiddenField.dom.value =
12262                 this.hiddenValue !== undefined ? this.hiddenValue :
12263                 this.value !== undefined ? this.value : '';
12264
12265             // prevent input submission
12266             this.el.dom.removeAttribute('name');
12267             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12268              
12269              
12270         }
12271         //if(Roo.isGecko){
12272         //    this.el.dom.setAttribute('autocomplete', 'off');
12273         //}
12274         
12275         var cls = 'x-combo-list';
12276         
12277         //this.list = new Roo.Layer({
12278         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12279         //});
12280         
12281         var _this = this;
12282         
12283         (function(){
12284             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12285             _this.list.setWidth(lw);
12286         }).defer(100);
12287         
12288         this.list.on('mouseover', this.onViewOver, this);
12289         this.list.on('mousemove', this.onViewMove, this);
12290         
12291         this.list.on('scroll', this.onViewScroll, this);
12292         
12293         /*
12294         this.list.swallowEvent('mousewheel');
12295         this.assetHeight = 0;
12296
12297         if(this.title){
12298             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12299             this.assetHeight += this.header.getHeight();
12300         }
12301
12302         this.innerList = this.list.createChild({cls:cls+'-inner'});
12303         this.innerList.on('mouseover', this.onViewOver, this);
12304         this.innerList.on('mousemove', this.onViewMove, this);
12305         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12306         
12307         if(this.allowBlank && !this.pageSize && !this.disableClear){
12308             this.footer = this.list.createChild({cls:cls+'-ft'});
12309             this.pageTb = new Roo.Toolbar(this.footer);
12310            
12311         }
12312         if(this.pageSize){
12313             this.footer = this.list.createChild({cls:cls+'-ft'});
12314             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12315                     {pageSize: this.pageSize});
12316             
12317         }
12318         
12319         if (this.pageTb && this.allowBlank && !this.disableClear) {
12320             var _this = this;
12321             this.pageTb.add(new Roo.Toolbar.Fill(), {
12322                 cls: 'x-btn-icon x-btn-clear',
12323                 text: '&#160;',
12324                 handler: function()
12325                 {
12326                     _this.collapse();
12327                     _this.clearValue();
12328                     _this.onSelect(false, -1);
12329                 }
12330             });
12331         }
12332         if (this.footer) {
12333             this.assetHeight += this.footer.getHeight();
12334         }
12335         */
12336             
12337         if(!this.tpl){
12338             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12339         }
12340
12341         this.view = new Roo.View(this.list, this.tpl, {
12342             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12343         });
12344         //this.view.wrapEl.setDisplayed(false);
12345         this.view.on('click', this.onViewClick, this);
12346         
12347         
12348         
12349         this.store.on('beforeload', this.onBeforeLoad, this);
12350         this.store.on('load', this.onLoad, this);
12351         this.store.on('loadexception', this.onLoadException, this);
12352         /*
12353         if(this.resizable){
12354             this.resizer = new Roo.Resizable(this.list,  {
12355                pinned:true, handles:'se'
12356             });
12357             this.resizer.on('resize', function(r, w, h){
12358                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12359                 this.listWidth = w;
12360                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12361                 this.restrictHeight();
12362             }, this);
12363             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12364         }
12365         */
12366         if(!this.editable){
12367             this.editable = true;
12368             this.setEditable(false);
12369         }
12370         
12371         /*
12372         
12373         if (typeof(this.events.add.listeners) != 'undefined') {
12374             
12375             this.addicon = this.wrap.createChild(
12376                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12377        
12378             this.addicon.on('click', function(e) {
12379                 this.fireEvent('add', this);
12380             }, this);
12381         }
12382         if (typeof(this.events.edit.listeners) != 'undefined') {
12383             
12384             this.editicon = this.wrap.createChild(
12385                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12386             if (this.addicon) {
12387                 this.editicon.setStyle('margin-left', '40px');
12388             }
12389             this.editicon.on('click', function(e) {
12390                 
12391                 // we fire even  if inothing is selected..
12392                 this.fireEvent('edit', this, this.lastData );
12393                 
12394             }, this);
12395         }
12396         */
12397         
12398         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12399             "up" : function(e){
12400                 this.inKeyMode = true;
12401                 this.selectPrev();
12402             },
12403
12404             "down" : function(e){
12405                 if(!this.isExpanded()){
12406                     this.onTriggerClick();
12407                 }else{
12408                     this.inKeyMode = true;
12409                     this.selectNext();
12410                 }
12411             },
12412
12413             "enter" : function(e){
12414 //                this.onViewClick();
12415                 //return true;
12416                 this.collapse();
12417                 
12418                 if(this.fireEvent("specialkey", this, e)){
12419                     this.onViewClick(false);
12420                 }
12421                 
12422                 return true;
12423             },
12424
12425             "esc" : function(e){
12426                 this.collapse();
12427             },
12428
12429             "tab" : function(e){
12430                 this.collapse();
12431                 
12432                 if(this.fireEvent("specialkey", this, e)){
12433                     this.onViewClick(false);
12434                 }
12435                 
12436                 return true;
12437             },
12438
12439             scope : this,
12440
12441             doRelay : function(foo, bar, hname){
12442                 if(hname == 'down' || this.scope.isExpanded()){
12443                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12444                 }
12445                 return true;
12446             },
12447
12448             forceKeyDown: true
12449         });
12450         
12451         
12452         this.queryDelay = Math.max(this.queryDelay || 10,
12453                 this.mode == 'local' ? 10 : 250);
12454         
12455         
12456         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12457         
12458         if(this.typeAhead){
12459             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12460         }
12461         if(this.editable !== false){
12462             this.inputEl().on("keyup", this.onKeyUp, this);
12463         }
12464         if(this.forceSelection){
12465             this.inputEl().on('blur', this.doForce, this);
12466         }
12467         
12468         if(this.multiple){
12469             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12470             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12471         }
12472     },
12473     
12474     initTickableEvents: function()
12475     {   
12476         this.createList();
12477         
12478         if(this.hiddenName){
12479             
12480             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12481             
12482             this.hiddenField.dom.value =
12483                 this.hiddenValue !== undefined ? this.hiddenValue :
12484                 this.value !== undefined ? this.value : '';
12485
12486             // prevent input submission
12487             this.el.dom.removeAttribute('name');
12488             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12489              
12490              
12491         }
12492         
12493 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12494         
12495         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12496         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12497         if(this.triggerList){
12498             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12499         }
12500          
12501         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12502         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12503         
12504         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12505         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12506         
12507         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12508         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12509         
12510         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12511         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12512         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12513         
12514         this.okBtn.hide();
12515         this.cancelBtn.hide();
12516         
12517         var _this = this;
12518         
12519         (function(){
12520             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12521             _this.list.setWidth(lw);
12522         }).defer(100);
12523         
12524         this.list.on('mouseover', this.onViewOver, this);
12525         this.list.on('mousemove', this.onViewMove, this);
12526         
12527         this.list.on('scroll', this.onViewScroll, this);
12528         
12529         if(!this.tpl){
12530             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>';
12531         }
12532
12533         this.view = new Roo.View(this.list, this.tpl, {
12534             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12535         });
12536         
12537         //this.view.wrapEl.setDisplayed(false);
12538         this.view.on('click', this.onViewClick, this);
12539         
12540         
12541         
12542         this.store.on('beforeload', this.onBeforeLoad, this);
12543         this.store.on('load', this.onLoad, this);
12544         this.store.on('loadexception', this.onLoadException, this);
12545         
12546         if(this.editable){
12547             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12548                 "up" : function(e){
12549                     this.inKeyMode = true;
12550                     this.selectPrev();
12551                 },
12552
12553                 "down" : function(e){
12554                     this.inKeyMode = true;
12555                     this.selectNext();
12556                 },
12557
12558                 "enter" : function(e){
12559                     if(this.fireEvent("specialkey", this, e)){
12560                         this.onViewClick(false);
12561                     }
12562                     
12563                     return true;
12564                 },
12565
12566                 "esc" : function(e){
12567                     this.onTickableFooterButtonClick(e, false, false);
12568                 },
12569
12570                 "tab" : function(e){
12571                     this.fireEvent("specialkey", this, e);
12572                     
12573                     this.onTickableFooterButtonClick(e, false, false);
12574                     
12575                     return true;
12576                 },
12577
12578                 scope : this,
12579
12580                 doRelay : function(e, fn, key){
12581                     if(this.scope.isExpanded()){
12582                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12583                     }
12584                     return true;
12585                 },
12586
12587                 forceKeyDown: true
12588             });
12589         }
12590         
12591         this.queryDelay = Math.max(this.queryDelay || 10,
12592                 this.mode == 'local' ? 10 : 250);
12593         
12594         
12595         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12596         
12597         if(this.typeAhead){
12598             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12599         }
12600         
12601         if(this.editable !== false){
12602             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12603         }
12604         
12605     },
12606
12607     onDestroy : function(){
12608         if(this.view){
12609             this.view.setStore(null);
12610             this.view.el.removeAllListeners();
12611             this.view.el.remove();
12612             this.view.purgeListeners();
12613         }
12614         if(this.list){
12615             this.list.dom.innerHTML  = '';
12616         }
12617         
12618         if(this.store){
12619             this.store.un('beforeload', this.onBeforeLoad, this);
12620             this.store.un('load', this.onLoad, this);
12621             this.store.un('loadexception', this.onLoadException, this);
12622         }
12623         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12624     },
12625
12626     // private
12627     fireKey : function(e){
12628         if(e.isNavKeyPress() && !this.list.isVisible()){
12629             this.fireEvent("specialkey", this, e);
12630         }
12631     },
12632
12633     // private
12634     onResize: function(w, h){
12635 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12636 //        
12637 //        if(typeof w != 'number'){
12638 //            // we do not handle it!?!?
12639 //            return;
12640 //        }
12641 //        var tw = this.trigger.getWidth();
12642 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12643 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12644 //        var x = w - tw;
12645 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12646 //            
12647 //        //this.trigger.setStyle('left', x+'px');
12648 //        
12649 //        if(this.list && this.listWidth === undefined){
12650 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12651 //            this.list.setWidth(lw);
12652 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12653 //        }
12654         
12655     
12656         
12657     },
12658
12659     /**
12660      * Allow or prevent the user from directly editing the field text.  If false is passed,
12661      * the user will only be able to select from the items defined in the dropdown list.  This method
12662      * is the runtime equivalent of setting the 'editable' config option at config time.
12663      * @param {Boolean} value True to allow the user to directly edit the field text
12664      */
12665     setEditable : function(value){
12666         if(value == this.editable){
12667             return;
12668         }
12669         this.editable = value;
12670         if(!value){
12671             this.inputEl().dom.setAttribute('readOnly', true);
12672             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12673             this.inputEl().addClass('x-combo-noedit');
12674         }else{
12675             this.inputEl().dom.setAttribute('readOnly', false);
12676             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12677             this.inputEl().removeClass('x-combo-noedit');
12678         }
12679     },
12680
12681     // private
12682     
12683     onBeforeLoad : function(combo,opts){
12684         if(!this.hasFocus){
12685             return;
12686         }
12687          if (!opts.add) {
12688             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12689          }
12690         this.restrictHeight();
12691         this.selectedIndex = -1;
12692     },
12693
12694     // private
12695     onLoad : function(){
12696         
12697         this.hasQuery = false;
12698         
12699         if(!this.hasFocus){
12700             return;
12701         }
12702         
12703         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12704             this.loading.hide();
12705         }
12706              
12707         if(this.store.getCount() > 0){
12708             this.expand();
12709             this.restrictHeight();
12710             if(this.lastQuery == this.allQuery){
12711                 if(this.editable && !this.tickable){
12712                     this.inputEl().dom.select();
12713                 }
12714                 
12715                 if(
12716                     !this.selectByValue(this.value, true) &&
12717                     this.autoFocus && 
12718                     (
12719                         !this.store.lastOptions ||
12720                         typeof(this.store.lastOptions.add) == 'undefined' || 
12721                         this.store.lastOptions.add != true
12722                     )
12723                 ){
12724                     this.select(0, true);
12725                 }
12726             }else{
12727                 if(this.autoFocus){
12728                     this.selectNext();
12729                 }
12730                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12731                     this.taTask.delay(this.typeAheadDelay);
12732                 }
12733             }
12734         }else{
12735             this.onEmptyResults();
12736         }
12737         
12738         //this.el.focus();
12739     },
12740     // private
12741     onLoadException : function()
12742     {
12743         this.hasQuery = false;
12744         
12745         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12746             this.loading.hide();
12747         }
12748         
12749         if(this.tickable && this.editable){
12750             return;
12751         }
12752         
12753         this.collapse();
12754         // only causes errors at present
12755         //Roo.log(this.store.reader.jsonData);
12756         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12757             // fixme
12758             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12759         //}
12760         
12761         
12762     },
12763     // private
12764     onTypeAhead : function(){
12765         if(this.store.getCount() > 0){
12766             var r = this.store.getAt(0);
12767             var newValue = r.data[this.displayField];
12768             var len = newValue.length;
12769             var selStart = this.getRawValue().length;
12770             
12771             if(selStart != len){
12772                 this.setRawValue(newValue);
12773                 this.selectText(selStart, newValue.length);
12774             }
12775         }
12776     },
12777
12778     // private
12779     onSelect : function(record, index){
12780         
12781         if(this.fireEvent('beforeselect', this, record, index) !== false){
12782         
12783             this.setFromData(index > -1 ? record.data : false);
12784             
12785             this.collapse();
12786             this.fireEvent('select', this, record, index);
12787         }
12788     },
12789
12790     /**
12791      * Returns the currently selected field value or empty string if no value is set.
12792      * @return {String} value The selected value
12793      */
12794     getValue : function(){
12795         
12796         if(this.multiple){
12797             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12798         }
12799         
12800         if(this.valueField){
12801             return typeof this.value != 'undefined' ? this.value : '';
12802         }else{
12803             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12804         }
12805     },
12806
12807     /**
12808      * Clears any text/value currently set in the field
12809      */
12810     clearValue : function(){
12811         if(this.hiddenField){
12812             this.hiddenField.dom.value = '';
12813         }
12814         this.value = '';
12815         this.setRawValue('');
12816         this.lastSelectionText = '';
12817         this.lastData = false;
12818         
12819         var close = this.closeTriggerEl();
12820         
12821         if(close){
12822             close.hide();
12823         }
12824         
12825     },
12826
12827     /**
12828      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12829      * will be displayed in the field.  If the value does not match the data value of an existing item,
12830      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12831      * Otherwise the field will be blank (although the value will still be set).
12832      * @param {String} value The value to match
12833      */
12834     setValue : function(v){
12835         if(this.multiple){
12836             this.syncValue();
12837             return;
12838         }
12839         
12840         var text = v;
12841         if(this.valueField){
12842             var r = this.findRecord(this.valueField, v);
12843             if(r){
12844                 text = r.data[this.displayField];
12845             }else if(this.valueNotFoundText !== undefined){
12846                 text = this.valueNotFoundText;
12847             }
12848         }
12849         this.lastSelectionText = text;
12850         if(this.hiddenField){
12851             this.hiddenField.dom.value = v;
12852         }
12853         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12854         this.value = v;
12855         
12856         var close = this.closeTriggerEl();
12857         
12858         if(close){
12859             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12860         }
12861     },
12862     /**
12863      * @property {Object} the last set data for the element
12864      */
12865     
12866     lastData : false,
12867     /**
12868      * Sets the value of the field based on a object which is related to the record format for the store.
12869      * @param {Object} value the value to set as. or false on reset?
12870      */
12871     setFromData : function(o){
12872         
12873         if(this.multiple){
12874             this.addItem(o);
12875             return;
12876         }
12877             
12878         var dv = ''; // display value
12879         var vv = ''; // value value..
12880         this.lastData = o;
12881         if (this.displayField) {
12882             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12883         } else {
12884             // this is an error condition!!!
12885             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12886         }
12887         
12888         if(this.valueField){
12889             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12890         }
12891         
12892         var close = this.closeTriggerEl();
12893         
12894         if(close){
12895             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12896         }
12897         
12898         if(this.hiddenField){
12899             this.hiddenField.dom.value = vv;
12900             
12901             this.lastSelectionText = dv;
12902             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12903             this.value = vv;
12904             return;
12905         }
12906         // no hidden field.. - we store the value in 'value', but still display
12907         // display field!!!!
12908         this.lastSelectionText = dv;
12909         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12910         this.value = vv;
12911         
12912         
12913         
12914     },
12915     // private
12916     reset : function(){
12917         // overridden so that last data is reset..
12918         
12919         if(this.multiple){
12920             this.clearItem();
12921             return;
12922         }
12923         
12924         this.setValue(this.originalValue);
12925         this.clearInvalid();
12926         this.lastData = false;
12927         if (this.view) {
12928             this.view.clearSelections();
12929         }
12930     },
12931     // private
12932     findRecord : function(prop, value){
12933         var record;
12934         if(this.store.getCount() > 0){
12935             this.store.each(function(r){
12936                 if(r.data[prop] == value){
12937                     record = r;
12938                     return false;
12939                 }
12940                 return true;
12941             });
12942         }
12943         return record;
12944     },
12945     
12946     getName: function()
12947     {
12948         // returns hidden if it's set..
12949         if (!this.rendered) {return ''};
12950         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12951         
12952     },
12953     // private
12954     onViewMove : function(e, t){
12955         this.inKeyMode = false;
12956     },
12957
12958     // private
12959     onViewOver : function(e, t){
12960         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12961             return;
12962         }
12963         var item = this.view.findItemFromChild(t);
12964         
12965         if(item){
12966             var index = this.view.indexOf(item);
12967             this.select(index, false);
12968         }
12969     },
12970
12971     // private
12972     onViewClick : function(view, doFocus, el, e)
12973     {
12974         var index = this.view.getSelectedIndexes()[0];
12975         
12976         var r = this.store.getAt(index);
12977         
12978         if(this.tickable){
12979             
12980             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12981                 return;
12982             }
12983             
12984             var rm = false;
12985             var _this = this;
12986             
12987             Roo.each(this.tickItems, function(v,k){
12988                 
12989                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12990                     Roo.log(v);
12991                     _this.tickItems.splice(k, 1);
12992                     
12993                     if(typeof(e) == 'undefined' && view == false){
12994                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12995                     }
12996                     
12997                     rm = true;
12998                     return;
12999                 }
13000             });
13001             
13002             if(rm){
13003                 return;
13004             }
13005             
13006             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13007                 this.tickItems.push(r.data);
13008             }
13009             
13010             if(typeof(e) == 'undefined' && view == false){
13011                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13012             }
13013                     
13014             return;
13015         }
13016         
13017         if(r){
13018             this.onSelect(r, index);
13019         }
13020         if(doFocus !== false && !this.blockFocus){
13021             this.inputEl().focus();
13022         }
13023     },
13024
13025     // private
13026     restrictHeight : function(){
13027         //this.innerList.dom.style.height = '';
13028         //var inner = this.innerList.dom;
13029         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13030         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13031         //this.list.beginUpdate();
13032         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13033         this.list.alignTo(this.inputEl(), this.listAlign);
13034         this.list.alignTo(this.inputEl(), this.listAlign);
13035         //this.list.endUpdate();
13036     },
13037
13038     // private
13039     onEmptyResults : function(){
13040         
13041         if(this.tickable && this.editable){
13042             this.restrictHeight();
13043             return;
13044         }
13045         
13046         this.collapse();
13047     },
13048
13049     /**
13050      * Returns true if the dropdown list is expanded, else false.
13051      */
13052     isExpanded : function(){
13053         return this.list.isVisible();
13054     },
13055
13056     /**
13057      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13058      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13059      * @param {String} value The data value of the item to select
13060      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13061      * selected item if it is not currently in view (defaults to true)
13062      * @return {Boolean} True if the value matched an item in the list, else false
13063      */
13064     selectByValue : function(v, scrollIntoView){
13065         if(v !== undefined && v !== null){
13066             var r = this.findRecord(this.valueField || this.displayField, v);
13067             if(r){
13068                 this.select(this.store.indexOf(r), scrollIntoView);
13069                 return true;
13070             }
13071         }
13072         return false;
13073     },
13074
13075     /**
13076      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13077      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13078      * @param {Number} index The zero-based index of the list item to select
13079      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13080      * selected item if it is not currently in view (defaults to true)
13081      */
13082     select : function(index, scrollIntoView){
13083         this.selectedIndex = index;
13084         this.view.select(index);
13085         if(scrollIntoView !== false){
13086             var el = this.view.getNode(index);
13087             /*
13088              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13089              */
13090             if(el){
13091                 this.list.scrollChildIntoView(el, false);
13092             }
13093         }
13094     },
13095
13096     // private
13097     selectNext : function(){
13098         var ct = this.store.getCount();
13099         if(ct > 0){
13100             if(this.selectedIndex == -1){
13101                 this.select(0);
13102             }else if(this.selectedIndex < ct-1){
13103                 this.select(this.selectedIndex+1);
13104             }
13105         }
13106     },
13107
13108     // private
13109     selectPrev : function(){
13110         var ct = this.store.getCount();
13111         if(ct > 0){
13112             if(this.selectedIndex == -1){
13113                 this.select(0);
13114             }else if(this.selectedIndex != 0){
13115                 this.select(this.selectedIndex-1);
13116             }
13117         }
13118     },
13119
13120     // private
13121     onKeyUp : function(e){
13122         if(this.editable !== false && !e.isSpecialKey()){
13123             this.lastKey = e.getKey();
13124             this.dqTask.delay(this.queryDelay);
13125         }
13126     },
13127
13128     // private
13129     validateBlur : function(){
13130         return !this.list || !this.list.isVisible();   
13131     },
13132
13133     // private
13134     initQuery : function(){
13135         
13136         var v = this.getRawValue();
13137         
13138         if(this.tickable && this.editable){
13139             v = this.tickableInputEl().getValue();
13140         }
13141         
13142         this.doQuery(v);
13143     },
13144
13145     // private
13146     doForce : function(){
13147         if(this.inputEl().dom.value.length > 0){
13148             this.inputEl().dom.value =
13149                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13150              
13151         }
13152     },
13153
13154     /**
13155      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13156      * query allowing the query action to be canceled if needed.
13157      * @param {String} query The SQL query to execute
13158      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13159      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13160      * saved in the current store (defaults to false)
13161      */
13162     doQuery : function(q, forceAll){
13163         
13164         if(q === undefined || q === null){
13165             q = '';
13166         }
13167         var qe = {
13168             query: q,
13169             forceAll: forceAll,
13170             combo: this,
13171             cancel:false
13172         };
13173         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13174             return false;
13175         }
13176         q = qe.query;
13177         
13178         forceAll = qe.forceAll;
13179         if(forceAll === true || (q.length >= this.minChars)){
13180             
13181             this.hasQuery = true;
13182             
13183             if(this.lastQuery != q || this.alwaysQuery){
13184                 this.lastQuery = q;
13185                 if(this.mode == 'local'){
13186                     this.selectedIndex = -1;
13187                     if(forceAll){
13188                         this.store.clearFilter();
13189                     }else{
13190                         
13191                         if(this.specialFilter){
13192                             this.fireEvent('specialfilter', this);
13193                             this.onLoad();
13194                             return;
13195                         }
13196                         
13197                         this.store.filter(this.displayField, q);
13198                     }
13199                     
13200                     this.store.fireEvent("datachanged", this.store);
13201                     
13202                     this.onLoad();
13203                     
13204                     
13205                 }else{
13206                     
13207                     this.store.baseParams[this.queryParam] = q;
13208                     
13209                     var options = {params : this.getParams(q)};
13210                     
13211                     if(this.loadNext){
13212                         options.add = true;
13213                         options.params.start = this.page * this.pageSize;
13214                     }
13215                     
13216                     this.store.load(options);
13217                     
13218                     /*
13219                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13220                      *  we should expand the list on onLoad
13221                      *  so command out it
13222                      */
13223 //                    this.expand();
13224                 }
13225             }else{
13226                 this.selectedIndex = -1;
13227                 this.onLoad();   
13228             }
13229         }
13230         
13231         this.loadNext = false;
13232     },
13233     
13234     // private
13235     getParams : function(q){
13236         var p = {};
13237         //p[this.queryParam] = q;
13238         
13239         if(this.pageSize){
13240             p.start = 0;
13241             p.limit = this.pageSize;
13242         }
13243         return p;
13244     },
13245
13246     /**
13247      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13248      */
13249     collapse : function(){
13250         if(!this.isExpanded()){
13251             return;
13252         }
13253         
13254         this.list.hide();
13255         
13256         if(this.tickable){
13257             this.hasFocus = false;
13258             this.okBtn.hide();
13259             this.cancelBtn.hide();
13260             this.trigger.show();
13261             
13262             if(this.editable){
13263                 this.tickableInputEl().dom.value = '';
13264                 this.tickableInputEl().blur();
13265             }
13266             
13267         }
13268         
13269         Roo.get(document).un('mousedown', this.collapseIf, this);
13270         Roo.get(document).un('mousewheel', this.collapseIf, this);
13271         if (!this.editable) {
13272             Roo.get(document).un('keydown', this.listKeyPress, this);
13273         }
13274         this.fireEvent('collapse', this);
13275     },
13276
13277     // private
13278     collapseIf : function(e){
13279         var in_combo  = e.within(this.el);
13280         var in_list =  e.within(this.list);
13281         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13282         
13283         if (in_combo || in_list || is_list) {
13284             //e.stopPropagation();
13285             return;
13286         }
13287         
13288         if(this.tickable){
13289             this.onTickableFooterButtonClick(e, false, false);
13290         }
13291
13292         this.collapse();
13293         
13294     },
13295
13296     /**
13297      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13298      */
13299     expand : function(){
13300        
13301         if(this.isExpanded() || !this.hasFocus){
13302             return;
13303         }
13304         
13305         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13306         this.list.setWidth(lw);
13307         
13308         
13309          Roo.log('expand');
13310         
13311         this.list.show();
13312         
13313         this.restrictHeight();
13314         
13315         if(this.tickable){
13316             
13317             this.tickItems = Roo.apply([], this.item);
13318             
13319             this.okBtn.show();
13320             this.cancelBtn.show();
13321             this.trigger.hide();
13322             
13323             if(this.editable){
13324                 this.tickableInputEl().focus();
13325             }
13326             
13327         }
13328         
13329         Roo.get(document).on('mousedown', this.collapseIf, this);
13330         Roo.get(document).on('mousewheel', this.collapseIf, this);
13331         if (!this.editable) {
13332             Roo.get(document).on('keydown', this.listKeyPress, this);
13333         }
13334         
13335         this.fireEvent('expand', this);
13336     },
13337
13338     // private
13339     // Implements the default empty TriggerField.onTriggerClick function
13340     onTriggerClick : function(e)
13341     {
13342         Roo.log('trigger click');
13343         
13344         if(this.disabled || !this.triggerList){
13345             return;
13346         }
13347         
13348         this.page = 0;
13349         this.loadNext = false;
13350         
13351         if(this.isExpanded()){
13352             this.collapse();
13353             if (!this.blockFocus) {
13354                 this.inputEl().focus();
13355             }
13356             
13357         }else {
13358             this.hasFocus = true;
13359             if(this.triggerAction == 'all') {
13360                 this.doQuery(this.allQuery, true);
13361             } else {
13362                 this.doQuery(this.getRawValue());
13363             }
13364             if (!this.blockFocus) {
13365                 this.inputEl().focus();
13366             }
13367         }
13368     },
13369     
13370     onTickableTriggerClick : function(e)
13371     {
13372         if(this.disabled){
13373             return;
13374         }
13375         
13376         this.page = 0;
13377         this.loadNext = false;
13378         this.hasFocus = true;
13379         
13380         if(this.triggerAction == 'all') {
13381             this.doQuery(this.allQuery, true);
13382         } else {
13383             this.doQuery(this.getRawValue());
13384         }
13385     },
13386     
13387     onSearchFieldClick : function(e)
13388     {
13389         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13390             this.onTickableFooterButtonClick(e, false, false);
13391             return;
13392         }
13393         
13394         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13395             return;
13396         }
13397         
13398         this.page = 0;
13399         this.loadNext = false;
13400         this.hasFocus = true;
13401         
13402         if(this.triggerAction == 'all') {
13403             this.doQuery(this.allQuery, true);
13404         } else {
13405             this.doQuery(this.getRawValue());
13406         }
13407     },
13408     
13409     listKeyPress : function(e)
13410     {
13411         //Roo.log('listkeypress');
13412         // scroll to first matching element based on key pres..
13413         if (e.isSpecialKey()) {
13414             return false;
13415         }
13416         var k = String.fromCharCode(e.getKey()).toUpperCase();
13417         //Roo.log(k);
13418         var match  = false;
13419         var csel = this.view.getSelectedNodes();
13420         var cselitem = false;
13421         if (csel.length) {
13422             var ix = this.view.indexOf(csel[0]);
13423             cselitem  = this.store.getAt(ix);
13424             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13425                 cselitem = false;
13426             }
13427             
13428         }
13429         
13430         this.store.each(function(v) { 
13431             if (cselitem) {
13432                 // start at existing selection.
13433                 if (cselitem.id == v.id) {
13434                     cselitem = false;
13435                 }
13436                 return true;
13437             }
13438                 
13439             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13440                 match = this.store.indexOf(v);
13441                 return false;
13442             }
13443             return true;
13444         }, this);
13445         
13446         if (match === false) {
13447             return true; // no more action?
13448         }
13449         // scroll to?
13450         this.view.select(match);
13451         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13452         sn.scrollIntoView(sn.dom.parentNode, false);
13453     },
13454     
13455     onViewScroll : function(e, t){
13456         
13457         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){
13458             return;
13459         }
13460         
13461         this.hasQuery = true;
13462         
13463         this.loading = this.list.select('.loading', true).first();
13464         
13465         if(this.loading === null){
13466             this.list.createChild({
13467                 tag: 'div',
13468                 cls: 'loading roo-select2-more-results roo-select2-active',
13469                 html: 'Loading more results...'
13470             });
13471             
13472             this.loading = this.list.select('.loading', true).first();
13473             
13474             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13475             
13476             this.loading.hide();
13477         }
13478         
13479         this.loading.show();
13480         
13481         var _combo = this;
13482         
13483         this.page++;
13484         this.loadNext = true;
13485         
13486         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13487         
13488         return;
13489     },
13490     
13491     addItem : function(o)
13492     {   
13493         var dv = ''; // display value
13494         
13495         if (this.displayField) {
13496             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13497         } else {
13498             // this is an error condition!!!
13499             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13500         }
13501         
13502         if(!dv.length){
13503             return;
13504         }
13505         
13506         var choice = this.choices.createChild({
13507             tag: 'li',
13508             cls: 'roo-select2-search-choice',
13509             cn: [
13510                 {
13511                     tag: 'div',
13512                     html: dv
13513                 },
13514                 {
13515                     tag: 'a',
13516                     href: '#',
13517                     cls: 'roo-select2-search-choice-close',
13518                     tabindex: '-1'
13519                 }
13520             ]
13521             
13522         }, this.searchField);
13523         
13524         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13525         
13526         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13527         
13528         this.item.push(o);
13529         
13530         this.lastData = o;
13531         
13532         this.syncValue();
13533         
13534         this.inputEl().dom.value = '';
13535         
13536         this.validate();
13537     },
13538     
13539     onRemoveItem : function(e, _self, o)
13540     {
13541         e.preventDefault();
13542         
13543         this.lastItem = Roo.apply([], this.item);
13544         
13545         var index = this.item.indexOf(o.data) * 1;
13546         
13547         if( index < 0){
13548             Roo.log('not this item?!');
13549             return;
13550         }
13551         
13552         this.item.splice(index, 1);
13553         o.item.remove();
13554         
13555         this.syncValue();
13556         
13557         this.fireEvent('remove', this, e);
13558         
13559         this.validate();
13560         
13561     },
13562     
13563     syncValue : function()
13564     {
13565         if(!this.item.length){
13566             this.clearValue();
13567             return;
13568         }
13569             
13570         var value = [];
13571         var _this = this;
13572         Roo.each(this.item, function(i){
13573             if(_this.valueField){
13574                 value.push(i[_this.valueField]);
13575                 return;
13576             }
13577
13578             value.push(i);
13579         });
13580
13581         this.value = value.join(',');
13582
13583         if(this.hiddenField){
13584             this.hiddenField.dom.value = this.value;
13585         }
13586         
13587         this.store.fireEvent("datachanged", this.store);
13588     },
13589     
13590     clearItem : function()
13591     {
13592         if(!this.multiple){
13593             return;
13594         }
13595         
13596         this.item = [];
13597         
13598         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13599            c.remove();
13600         });
13601         
13602         this.syncValue();
13603         
13604         this.validate();
13605         
13606         if(this.tickable && !Roo.isTouch){
13607             this.view.refresh();
13608         }
13609     },
13610     
13611     inputEl: function ()
13612     {
13613         if(Roo.isTouch && this.mobileTouchView){
13614             return this.el.select('input.form-control',true).first();
13615         }
13616         
13617         if(this.tickable){
13618             return this.searchField;
13619         }
13620         
13621         return this.el.select('input.form-control',true).first();
13622     },
13623     
13624     
13625     onTickableFooterButtonClick : function(e, btn, el)
13626     {
13627         e.preventDefault();
13628         
13629         this.lastItem = Roo.apply([], this.item);
13630         
13631         if(btn && btn.name == 'cancel'){
13632             this.tickItems = Roo.apply([], this.item);
13633             this.collapse();
13634             return;
13635         }
13636         
13637         this.clearItem();
13638         
13639         var _this = this;
13640         
13641         Roo.each(this.tickItems, function(o){
13642             _this.addItem(o);
13643         });
13644         
13645         this.collapse();
13646         
13647     },
13648     
13649     validate : function()
13650     {
13651         var v = this.getRawValue();
13652         
13653         if(this.multiple){
13654             v = this.getValue();
13655         }
13656         
13657         if(this.disabled || this.allowBlank || v.length){
13658             this.markValid();
13659             return true;
13660         }
13661         
13662         this.markInvalid();
13663         return false;
13664     },
13665     
13666     tickableInputEl : function()
13667     {
13668         if(!this.tickable || !this.editable){
13669             return this.inputEl();
13670         }
13671         
13672         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13673     },
13674     
13675     
13676     getAutoCreateTouchView : function()
13677     {
13678         var id = Roo.id();
13679         
13680         var cfg = {
13681             cls: 'form-group' //input-group
13682         };
13683         
13684         var input =  {
13685             tag: 'input',
13686             id : id,
13687             type : this.inputType,
13688             cls : 'form-control x-combo-noedit',
13689             autocomplete: 'new-password',
13690             placeholder : this.placeholder || '',
13691             readonly : true
13692         };
13693         
13694         if (this.name) {
13695             input.name = this.name;
13696         }
13697         
13698         if (this.size) {
13699             input.cls += ' input-' + this.size;
13700         }
13701         
13702         if (this.disabled) {
13703             input.disabled = true;
13704         }
13705         
13706         var inputblock = {
13707             cls : '',
13708             cn : [
13709                 input
13710             ]
13711         };
13712         
13713         if(this.before){
13714             inputblock.cls += ' input-group';
13715             
13716             inputblock.cn.unshift({
13717                 tag :'span',
13718                 cls : 'input-group-addon',
13719                 html : this.before
13720             });
13721         }
13722         
13723         if(this.removable && !this.multiple){
13724             inputblock.cls += ' roo-removable';
13725             
13726             inputblock.cn.push({
13727                 tag: 'button',
13728                 html : 'x',
13729                 cls : 'roo-combo-removable-btn close'
13730             });
13731         }
13732
13733         if(this.hasFeedback && !this.allowBlank){
13734             
13735             inputblock.cls += ' has-feedback';
13736             
13737             inputblock.cn.push({
13738                 tag: 'span',
13739                 cls: 'glyphicon form-control-feedback'
13740             });
13741             
13742         }
13743         
13744         if (this.after) {
13745             
13746             inputblock.cls += (this.before) ? '' : ' input-group';
13747             
13748             inputblock.cn.push({
13749                 tag :'span',
13750                 cls : 'input-group-addon',
13751                 html : this.after
13752             });
13753         }
13754
13755         var box = {
13756             tag: 'div',
13757             cn: [
13758                 {
13759                     tag: 'input',
13760                     type : 'hidden',
13761                     cls: 'form-hidden-field'
13762                 },
13763                 inputblock
13764             ]
13765             
13766         };
13767         
13768         if(this.multiple){
13769             box = {
13770                 tag: 'div',
13771                 cn: [
13772                     {
13773                         tag: 'input',
13774                         type : 'hidden',
13775                         cls: 'form-hidden-field'
13776                     },
13777                     {
13778                         tag: 'ul',
13779                         cls: 'roo-select2-choices',
13780                         cn:[
13781                             {
13782                                 tag: 'li',
13783                                 cls: 'roo-select2-search-field',
13784                                 cn: [
13785
13786                                     inputblock
13787                                 ]
13788                             }
13789                         ]
13790                     }
13791                 ]
13792             }
13793         };
13794         
13795         var combobox = {
13796             cls: 'roo-select2-container input-group',
13797             cn: [
13798                 box
13799             ]
13800         };
13801         
13802         if(this.multiple){
13803             combobox.cls += ' roo-select2-container-multi';
13804         }
13805         
13806         var align = this.labelAlign || this.parentLabelAlign();
13807         
13808         cfg.cn = combobox;
13809         
13810         if(this.fieldLabel.length){
13811             
13812             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13813             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13814             
13815             cfg.cn = [
13816                 {
13817                     tag: 'label',
13818                     cls : 'control-label ' + lw,
13819                     html : this.fieldLabel
13820
13821                 },
13822                 {
13823                     cls : cw, 
13824                     cn: [
13825                         combobox
13826                     ]
13827                 }
13828             ];
13829         }
13830         
13831         var settings = this;
13832         
13833         ['xs','sm','md','lg'].map(function(size){
13834             if (settings[size]) {
13835                 cfg.cls += ' col-' + size + '-' + settings[size];
13836             }
13837         });
13838         
13839         return cfg;
13840     },
13841     
13842     initTouchView : function()
13843     {
13844         this.renderTouchView();
13845         
13846         this.touchViewEl.on('scroll', function(){
13847             this.el.dom.scrollTop = 0;
13848         }, this);
13849         
13850         this.originalValue = this.getValue();
13851         
13852         this.inputEl().on("click", this.showTouchView, this);
13853         
13854         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13855         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13856         
13857         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13858         
13859         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13860         this.store.on('load', this.onTouchViewLoad, this);
13861         this.store.on('loadexception', this.onTouchViewLoadException, this);
13862         
13863         if(this.hiddenName){
13864             
13865             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13866             
13867             this.hiddenField.dom.value =
13868                 this.hiddenValue !== undefined ? this.hiddenValue :
13869                 this.value !== undefined ? this.value : '';
13870         
13871             this.el.dom.removeAttribute('name');
13872             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13873         }
13874         
13875         if(this.multiple){
13876             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13877             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13878         }
13879         
13880         if(this.removable && !this.multiple){
13881             var close = this.closeTriggerEl();
13882             if(close){
13883                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13884                 close.on('click', this.removeBtnClick, this, close);
13885             }
13886         }
13887         /*
13888          * fix the bug in Safari iOS8
13889          */
13890         this.inputEl().on("focus", function(e){
13891             document.activeElement.blur();
13892         }, this);
13893         
13894         return;
13895         
13896         
13897     },
13898     
13899     renderTouchView : function()
13900     {
13901         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13902         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13903         
13904         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13905         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13906         
13907         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13908         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13909         this.touchViewBodyEl.setStyle('overflow', 'auto');
13910         
13911         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13912         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13913         
13914         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13915         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13916         
13917     },
13918     
13919     showTouchView : function()
13920     {
13921         if(this.disabled){
13922             return;
13923         }
13924         
13925         this.touchViewHeaderEl.hide();
13926
13927         if(this.fieldLabel.length){
13928             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13929             this.touchViewHeaderEl.show();
13930         }
13931
13932         this.touchViewEl.show();
13933
13934         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13935         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13936
13937         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13938
13939         if(this.fieldLabel.length){
13940             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13941         }
13942         
13943         this.touchViewBodyEl.setHeight(bodyHeight);
13944
13945         if(this.animate){
13946             var _this = this;
13947             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13948         }else{
13949             this.touchViewEl.addClass('in');
13950         }
13951
13952         this.doTouchViewQuery();
13953         
13954     },
13955     
13956     hideTouchView : function()
13957     {
13958         this.touchViewEl.removeClass('in');
13959
13960         if(this.animate){
13961             var _this = this;
13962             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13963         }else{
13964             this.touchViewEl.setStyle('display', 'none');
13965         }
13966         
13967     },
13968     
13969     setTouchViewValue : function()
13970     {
13971         if(this.multiple){
13972             this.clearItem();
13973         
13974             var _this = this;
13975
13976             Roo.each(this.tickItems, function(o){
13977                 this.addItem(o);
13978             }, this);
13979         }
13980         
13981         this.hideTouchView();
13982     },
13983     
13984     doTouchViewQuery : function()
13985     {
13986         var qe = {
13987             query: '',
13988             forceAll: true,
13989             combo: this,
13990             cancel:false
13991         };
13992         
13993         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13994             return false;
13995         }
13996         
13997         if(!this.alwaysQuery || this.mode == 'local'){
13998             this.onTouchViewLoad();
13999             return;
14000         }
14001         
14002         this.store.load();
14003     },
14004     
14005     onTouchViewBeforeLoad : function(combo,opts)
14006     {
14007         return;
14008     },
14009
14010     // private
14011     onTouchViewLoad : function()
14012     {
14013         if(this.store.getCount() < 1){
14014             this.onTouchViewEmptyResults();
14015             return;
14016         }
14017         
14018         this.clearTouchView();
14019         
14020         var rawValue = this.getRawValue();
14021         
14022         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14023         
14024         this.tickItems = [];
14025         
14026         this.store.data.each(function(d, rowIndex){
14027             var row = this.touchViewListGroup.createChild(template);
14028             
14029             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14030                 row.addClass(d.data.cls);
14031             }
14032             
14033             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14034                 var cfg = {
14035                     data : d.data,
14036                     html : d.data[this.displayField]
14037                 };
14038                 
14039                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14040                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14041                 }
14042             }
14043             
14044             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14045                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14046             }
14047             
14048             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14049                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14050                 this.tickItems.push(d.data);
14051             }
14052             
14053             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14054             
14055         }, this);
14056         
14057         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14058         
14059         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14060
14061         if(this.fieldLabel.length){
14062             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14063         }
14064
14065         var listHeight = this.touchViewListGroup.getHeight();
14066         
14067         var _this = this;
14068         
14069         if(firstChecked && listHeight > bodyHeight){
14070             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14071         }
14072         
14073     },
14074     
14075     onTouchViewLoadException : function()
14076     {
14077         this.hideTouchView();
14078     },
14079     
14080     onTouchViewEmptyResults : function()
14081     {
14082         this.clearTouchView();
14083         
14084         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14085         
14086         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14087         
14088     },
14089     
14090     clearTouchView : function()
14091     {
14092         this.touchViewListGroup.dom.innerHTML = '';
14093     },
14094     
14095     onTouchViewClick : function(e, el, o)
14096     {
14097         e.preventDefault();
14098         
14099         var row = o.row;
14100         var rowIndex = o.rowIndex;
14101         
14102         var r = this.store.getAt(rowIndex);
14103         
14104         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14105             
14106             if(!this.multiple){
14107                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14108                     c.dom.removeAttribute('checked');
14109                 }, this);
14110
14111                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14112
14113                 this.setFromData(r.data);
14114
14115                 var close = this.closeTriggerEl();
14116
14117                 if(close){
14118                     close.show();
14119                 }
14120
14121                 this.hideTouchView();
14122
14123                 this.fireEvent('select', this, r, rowIndex);
14124
14125                 return;
14126             }
14127
14128             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14129                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14130                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14131                 return;
14132             }
14133
14134             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14135             this.addItem(r.data);
14136             this.tickItems.push(r.data);
14137         }
14138     }
14139     
14140
14141     /** 
14142     * @cfg {Boolean} grow 
14143     * @hide 
14144     */
14145     /** 
14146     * @cfg {Number} growMin 
14147     * @hide 
14148     */
14149     /** 
14150     * @cfg {Number} growMax 
14151     * @hide 
14152     */
14153     /**
14154      * @hide
14155      * @method autoSize
14156      */
14157 });
14158
14159 Roo.apply(Roo.bootstrap.ComboBox,  {
14160     
14161     header : {
14162         tag: 'div',
14163         cls: 'modal-header',
14164         cn: [
14165             {
14166                 tag: 'h4',
14167                 cls: 'modal-title'
14168             }
14169         ]
14170     },
14171     
14172     body : {
14173         tag: 'div',
14174         cls: 'modal-body',
14175         cn: [
14176             {
14177                 tag: 'ul',
14178                 cls: 'list-group'
14179             }
14180         ]
14181     },
14182     
14183     listItemRadio : {
14184         tag: 'li',
14185         cls: 'list-group-item',
14186         cn: [
14187             {
14188                 tag: 'span',
14189                 cls: 'roo-combobox-list-group-item-value'
14190             },
14191             {
14192                 tag: 'div',
14193                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14194                 cn: [
14195                     {
14196                         tag: 'input',
14197                         type: 'radio'
14198                     },
14199                     {
14200                         tag: 'label'
14201                     }
14202                 ]
14203             }
14204         ]
14205     },
14206     
14207     listItemCheckbox : {
14208         tag: 'li',
14209         cls: 'list-group-item',
14210         cn: [
14211             {
14212                 tag: 'span',
14213                 cls: 'roo-combobox-list-group-item-value'
14214             },
14215             {
14216                 tag: 'div',
14217                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14218                 cn: [
14219                     {
14220                         tag: 'input',
14221                         type: 'checkbox'
14222                     },
14223                     {
14224                         tag: 'label'
14225                     }
14226                 ]
14227             }
14228         ]
14229     },
14230     
14231     emptyResult : {
14232         tag: 'div',
14233         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14234     },
14235     
14236     footer : {
14237         tag: 'div',
14238         cls: 'modal-footer',
14239         cn: [
14240             {
14241                 tag: 'div',
14242                 cls: 'row',
14243                 cn: [
14244                     {
14245                         tag: 'div',
14246                         cls: 'col-xs-6 text-left',
14247                         cn: {
14248                             tag: 'button',
14249                             cls: 'btn btn-danger roo-touch-view-cancel',
14250                             html: 'Cancel'
14251                         }
14252                     },
14253                     {
14254                         tag: 'div',
14255                         cls: 'col-xs-6 text-right',
14256                         cn: {
14257                             tag: 'button',
14258                             cls: 'btn btn-success roo-touch-view-ok',
14259                             html: 'OK'
14260                         }
14261                     }
14262                 ]
14263             }
14264         ]
14265         
14266     }
14267 });
14268
14269 Roo.apply(Roo.bootstrap.ComboBox,  {
14270     
14271     touchViewTemplate : {
14272         tag: 'div',
14273         cls: 'modal fade roo-combobox-touch-view',
14274         cn: [
14275             {
14276                 tag: 'div',
14277                 cls: 'modal-dialog',
14278                 style : 'position:fixed', // we have to fix position....
14279                 cn: [
14280                     {
14281                         tag: 'div',
14282                         cls: 'modal-content',
14283                         cn: [
14284                             Roo.bootstrap.ComboBox.header,
14285                             Roo.bootstrap.ComboBox.body,
14286                             Roo.bootstrap.ComboBox.footer
14287                         ]
14288                     }
14289                 ]
14290             }
14291         ]
14292     }
14293 });/*
14294  * Based on:
14295  * Ext JS Library 1.1.1
14296  * Copyright(c) 2006-2007, Ext JS, LLC.
14297  *
14298  * Originally Released Under LGPL - original licence link has changed is not relivant.
14299  *
14300  * Fork - LGPL
14301  * <script type="text/javascript">
14302  */
14303
14304 /**
14305  * @class Roo.View
14306  * @extends Roo.util.Observable
14307  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14308  * This class also supports single and multi selection modes. <br>
14309  * Create a data model bound view:
14310  <pre><code>
14311  var store = new Roo.data.Store(...);
14312
14313  var view = new Roo.View({
14314     el : "my-element",
14315     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14316  
14317     singleSelect: true,
14318     selectedClass: "ydataview-selected",
14319     store: store
14320  });
14321
14322  // listen for node click?
14323  view.on("click", function(vw, index, node, e){
14324  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14325  });
14326
14327  // load XML data
14328  dataModel.load("foobar.xml");
14329  </code></pre>
14330  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14331  * <br><br>
14332  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14333  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14334  * 
14335  * Note: old style constructor is still suported (container, template, config)
14336  * 
14337  * @constructor
14338  * Create a new View
14339  * @param {Object} config The config object
14340  * 
14341  */
14342 Roo.View = function(config, depreciated_tpl, depreciated_config){
14343     
14344     this.parent = false;
14345     
14346     if (typeof(depreciated_tpl) == 'undefined') {
14347         // new way.. - universal constructor.
14348         Roo.apply(this, config);
14349         this.el  = Roo.get(this.el);
14350     } else {
14351         // old format..
14352         this.el  = Roo.get(config);
14353         this.tpl = depreciated_tpl;
14354         Roo.apply(this, depreciated_config);
14355     }
14356     this.wrapEl  = this.el.wrap().wrap();
14357     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14358     
14359     
14360     if(typeof(this.tpl) == "string"){
14361         this.tpl = new Roo.Template(this.tpl);
14362     } else {
14363         // support xtype ctors..
14364         this.tpl = new Roo.factory(this.tpl, Roo);
14365     }
14366     
14367     
14368     this.tpl.compile();
14369     
14370     /** @private */
14371     this.addEvents({
14372         /**
14373          * @event beforeclick
14374          * Fires before a click is processed. Returns false to cancel the default action.
14375          * @param {Roo.View} this
14376          * @param {Number} index The index of the target node
14377          * @param {HTMLElement} node The target node
14378          * @param {Roo.EventObject} e The raw event object
14379          */
14380             "beforeclick" : true,
14381         /**
14382          * @event click
14383          * Fires when a template node is clicked.
14384          * @param {Roo.View} this
14385          * @param {Number} index The index of the target node
14386          * @param {HTMLElement} node The target node
14387          * @param {Roo.EventObject} e The raw event object
14388          */
14389             "click" : true,
14390         /**
14391          * @event dblclick
14392          * Fires when a template node is double clicked.
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             "dblclick" : true,
14399         /**
14400          * @event contextmenu
14401          * Fires when a template node is right 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             "contextmenu" : true,
14408         /**
14409          * @event selectionchange
14410          * Fires when the selected nodes change.
14411          * @param {Roo.View} this
14412          * @param {Array} selections Array of the selected nodes
14413          */
14414             "selectionchange" : true,
14415     
14416         /**
14417          * @event beforeselect
14418          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14419          * @param {Roo.View} this
14420          * @param {HTMLElement} node The node to be selected
14421          * @param {Array} selections Array of currently selected nodes
14422          */
14423             "beforeselect" : true,
14424         /**
14425          * @event preparedata
14426          * Fires on every row to render, to allow you to change the data.
14427          * @param {Roo.View} this
14428          * @param {Object} data to be rendered (change this)
14429          */
14430           "preparedata" : true
14431           
14432           
14433         });
14434
14435
14436
14437     this.el.on({
14438         "click": this.onClick,
14439         "dblclick": this.onDblClick,
14440         "contextmenu": this.onContextMenu,
14441         scope:this
14442     });
14443
14444     this.selections = [];
14445     this.nodes = [];
14446     this.cmp = new Roo.CompositeElementLite([]);
14447     if(this.store){
14448         this.store = Roo.factory(this.store, Roo.data);
14449         this.setStore(this.store, true);
14450     }
14451     
14452     if ( this.footer && this.footer.xtype) {
14453            
14454          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14455         
14456         this.footer.dataSource = this.store;
14457         this.footer.container = fctr;
14458         this.footer = Roo.factory(this.footer, Roo);
14459         fctr.insertFirst(this.el);
14460         
14461         // this is a bit insane - as the paging toolbar seems to detach the el..
14462 //        dom.parentNode.parentNode.parentNode
14463          // they get detached?
14464     }
14465     
14466     
14467     Roo.View.superclass.constructor.call(this);
14468     
14469     
14470 };
14471
14472 Roo.extend(Roo.View, Roo.util.Observable, {
14473     
14474      /**
14475      * @cfg {Roo.data.Store} store Data store to load data from.
14476      */
14477     store : false,
14478     
14479     /**
14480      * @cfg {String|Roo.Element} el The container element.
14481      */
14482     el : '',
14483     
14484     /**
14485      * @cfg {String|Roo.Template} tpl The template used by this View 
14486      */
14487     tpl : false,
14488     /**
14489      * @cfg {String} dataName the named area of the template to use as the data area
14490      *                          Works with domtemplates roo-name="name"
14491      */
14492     dataName: false,
14493     /**
14494      * @cfg {String} selectedClass The css class to add to selected nodes
14495      */
14496     selectedClass : "x-view-selected",
14497      /**
14498      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14499      */
14500     emptyText : "",
14501     
14502     /**
14503      * @cfg {String} text to display on mask (default Loading)
14504      */
14505     mask : false,
14506     /**
14507      * @cfg {Boolean} multiSelect Allow multiple selection
14508      */
14509     multiSelect : false,
14510     /**
14511      * @cfg {Boolean} singleSelect Allow single selection
14512      */
14513     singleSelect:  false,
14514     
14515     /**
14516      * @cfg {Boolean} toggleSelect - selecting 
14517      */
14518     toggleSelect : false,
14519     
14520     /**
14521      * @cfg {Boolean} tickable - selecting 
14522      */
14523     tickable : false,
14524     
14525     /**
14526      * Returns the element this view is bound to.
14527      * @return {Roo.Element}
14528      */
14529     getEl : function(){
14530         return this.wrapEl;
14531     },
14532     
14533     
14534
14535     /**
14536      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14537      */
14538     refresh : function(){
14539         //Roo.log('refresh');
14540         var t = this.tpl;
14541         
14542         // if we are using something like 'domtemplate', then
14543         // the what gets used is:
14544         // t.applySubtemplate(NAME, data, wrapping data..)
14545         // the outer template then get' applied with
14546         //     the store 'extra data'
14547         // and the body get's added to the
14548         //      roo-name="data" node?
14549         //      <span class='roo-tpl-{name}'></span> ?????
14550         
14551         
14552         
14553         this.clearSelections();
14554         this.el.update("");
14555         var html = [];
14556         var records = this.store.getRange();
14557         if(records.length < 1) {
14558             
14559             // is this valid??  = should it render a template??
14560             
14561             this.el.update(this.emptyText);
14562             return;
14563         }
14564         var el = this.el;
14565         if (this.dataName) {
14566             this.el.update(t.apply(this.store.meta)); //????
14567             el = this.el.child('.roo-tpl-' + this.dataName);
14568         }
14569         
14570         for(var i = 0, len = records.length; i < len; i++){
14571             var data = this.prepareData(records[i].data, i, records[i]);
14572             this.fireEvent("preparedata", this, data, i, records[i]);
14573             
14574             var d = Roo.apply({}, data);
14575             
14576             if(this.tickable){
14577                 Roo.apply(d, {'roo-id' : Roo.id()});
14578                 
14579                 var _this = this;
14580             
14581                 Roo.each(this.parent.item, function(item){
14582                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14583                         return;
14584                     }
14585                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14586                 });
14587             }
14588             
14589             html[html.length] = Roo.util.Format.trim(
14590                 this.dataName ?
14591                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14592                     t.apply(d)
14593             );
14594         }
14595         
14596         
14597         
14598         el.update(html.join(""));
14599         this.nodes = el.dom.childNodes;
14600         this.updateIndexes(0);
14601     },
14602     
14603
14604     /**
14605      * Function to override to reformat the data that is sent to
14606      * the template for each node.
14607      * DEPRICATED - use the preparedata event handler.
14608      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14609      * a JSON object for an UpdateManager bound view).
14610      */
14611     prepareData : function(data, index, record)
14612     {
14613         this.fireEvent("preparedata", this, data, index, record);
14614         return data;
14615     },
14616
14617     onUpdate : function(ds, record){
14618         // Roo.log('on update');   
14619         this.clearSelections();
14620         var index = this.store.indexOf(record);
14621         var n = this.nodes[index];
14622         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14623         n.parentNode.removeChild(n);
14624         this.updateIndexes(index, index);
14625     },
14626
14627     
14628     
14629 // --------- FIXME     
14630     onAdd : function(ds, records, index)
14631     {
14632         //Roo.log(['on Add', ds, records, index] );        
14633         this.clearSelections();
14634         if(this.nodes.length == 0){
14635             this.refresh();
14636             return;
14637         }
14638         var n = this.nodes[index];
14639         for(var i = 0, len = records.length; i < len; i++){
14640             var d = this.prepareData(records[i].data, i, records[i]);
14641             if(n){
14642                 this.tpl.insertBefore(n, d);
14643             }else{
14644                 
14645                 this.tpl.append(this.el, d);
14646             }
14647         }
14648         this.updateIndexes(index);
14649     },
14650
14651     onRemove : function(ds, record, index){
14652        // Roo.log('onRemove');
14653         this.clearSelections();
14654         var el = this.dataName  ?
14655             this.el.child('.roo-tpl-' + this.dataName) :
14656             this.el; 
14657         
14658         el.dom.removeChild(this.nodes[index]);
14659         this.updateIndexes(index);
14660     },
14661
14662     /**
14663      * Refresh an individual node.
14664      * @param {Number} index
14665      */
14666     refreshNode : function(index){
14667         this.onUpdate(this.store, this.store.getAt(index));
14668     },
14669
14670     updateIndexes : function(startIndex, endIndex){
14671         var ns = this.nodes;
14672         startIndex = startIndex || 0;
14673         endIndex = endIndex || ns.length - 1;
14674         for(var i = startIndex; i <= endIndex; i++){
14675             ns[i].nodeIndex = i;
14676         }
14677     },
14678
14679     /**
14680      * Changes the data store this view uses and refresh the view.
14681      * @param {Store} store
14682      */
14683     setStore : function(store, initial){
14684         if(!initial && this.store){
14685             this.store.un("datachanged", this.refresh);
14686             this.store.un("add", this.onAdd);
14687             this.store.un("remove", this.onRemove);
14688             this.store.un("update", this.onUpdate);
14689             this.store.un("clear", this.refresh);
14690             this.store.un("beforeload", this.onBeforeLoad);
14691             this.store.un("load", this.onLoad);
14692             this.store.un("loadexception", this.onLoad);
14693         }
14694         if(store){
14695           
14696             store.on("datachanged", this.refresh, this);
14697             store.on("add", this.onAdd, this);
14698             store.on("remove", this.onRemove, this);
14699             store.on("update", this.onUpdate, this);
14700             store.on("clear", this.refresh, this);
14701             store.on("beforeload", this.onBeforeLoad, this);
14702             store.on("load", this.onLoad, this);
14703             store.on("loadexception", this.onLoad, this);
14704         }
14705         
14706         if(store){
14707             this.refresh();
14708         }
14709     },
14710     /**
14711      * onbeforeLoad - masks the loading area.
14712      *
14713      */
14714     onBeforeLoad : function(store,opts)
14715     {
14716          //Roo.log('onBeforeLoad');   
14717         if (!opts.add) {
14718             this.el.update("");
14719         }
14720         this.el.mask(this.mask ? this.mask : "Loading" ); 
14721     },
14722     onLoad : function ()
14723     {
14724         this.el.unmask();
14725     },
14726     
14727
14728     /**
14729      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14730      * @param {HTMLElement} node
14731      * @return {HTMLElement} The template node
14732      */
14733     findItemFromChild : function(node){
14734         var el = this.dataName  ?
14735             this.el.child('.roo-tpl-' + this.dataName,true) :
14736             this.el.dom; 
14737         
14738         if(!node || node.parentNode == el){
14739                     return node;
14740             }
14741             var p = node.parentNode;
14742             while(p && p != el){
14743             if(p.parentNode == el){
14744                 return p;
14745             }
14746             p = p.parentNode;
14747         }
14748             return null;
14749     },
14750
14751     /** @ignore */
14752     onClick : function(e){
14753         var item = this.findItemFromChild(e.getTarget());
14754         if(item){
14755             var index = this.indexOf(item);
14756             if(this.onItemClick(item, index, e) !== false){
14757                 this.fireEvent("click", this, index, item, e);
14758             }
14759         }else{
14760             this.clearSelections();
14761         }
14762     },
14763
14764     /** @ignore */
14765     onContextMenu : function(e){
14766         var item = this.findItemFromChild(e.getTarget());
14767         if(item){
14768             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14769         }
14770     },
14771
14772     /** @ignore */
14773     onDblClick : function(e){
14774         var item = this.findItemFromChild(e.getTarget());
14775         if(item){
14776             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14777         }
14778     },
14779
14780     onItemClick : function(item, index, e)
14781     {
14782         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14783             return false;
14784         }
14785         if (this.toggleSelect) {
14786             var m = this.isSelected(item) ? 'unselect' : 'select';
14787             //Roo.log(m);
14788             var _t = this;
14789             _t[m](item, true, false);
14790             return true;
14791         }
14792         if(this.multiSelect || this.singleSelect){
14793             if(this.multiSelect && e.shiftKey && this.lastSelection){
14794                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14795             }else{
14796                 this.select(item, this.multiSelect && e.ctrlKey);
14797                 this.lastSelection = item;
14798             }
14799             
14800             if(!this.tickable){
14801                 e.preventDefault();
14802             }
14803             
14804         }
14805         return true;
14806     },
14807
14808     /**
14809      * Get the number of selected nodes.
14810      * @return {Number}
14811      */
14812     getSelectionCount : function(){
14813         return this.selections.length;
14814     },
14815
14816     /**
14817      * Get the currently selected nodes.
14818      * @return {Array} An array of HTMLElements
14819      */
14820     getSelectedNodes : function(){
14821         return this.selections;
14822     },
14823
14824     /**
14825      * Get the indexes of the selected nodes.
14826      * @return {Array}
14827      */
14828     getSelectedIndexes : function(){
14829         var indexes = [], s = this.selections;
14830         for(var i = 0, len = s.length; i < len; i++){
14831             indexes.push(s[i].nodeIndex);
14832         }
14833         return indexes;
14834     },
14835
14836     /**
14837      * Clear all selections
14838      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14839      */
14840     clearSelections : function(suppressEvent){
14841         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14842             this.cmp.elements = this.selections;
14843             this.cmp.removeClass(this.selectedClass);
14844             this.selections = [];
14845             if(!suppressEvent){
14846                 this.fireEvent("selectionchange", this, this.selections);
14847             }
14848         }
14849     },
14850
14851     /**
14852      * Returns true if the passed node is selected
14853      * @param {HTMLElement/Number} node The node or node index
14854      * @return {Boolean}
14855      */
14856     isSelected : function(node){
14857         var s = this.selections;
14858         if(s.length < 1){
14859             return false;
14860         }
14861         node = this.getNode(node);
14862         return s.indexOf(node) !== -1;
14863     },
14864
14865     /**
14866      * Selects nodes.
14867      * @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
14868      * @param {Boolean} keepExisting (optional) true to keep existing selections
14869      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14870      */
14871     select : function(nodeInfo, keepExisting, suppressEvent){
14872         if(nodeInfo instanceof Array){
14873             if(!keepExisting){
14874                 this.clearSelections(true);
14875             }
14876             for(var i = 0, len = nodeInfo.length; i < len; i++){
14877                 this.select(nodeInfo[i], true, true);
14878             }
14879             return;
14880         } 
14881         var node = this.getNode(nodeInfo);
14882         if(!node || this.isSelected(node)){
14883             return; // already selected.
14884         }
14885         if(!keepExisting){
14886             this.clearSelections(true);
14887         }
14888         
14889         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14890             Roo.fly(node).addClass(this.selectedClass);
14891             this.selections.push(node);
14892             if(!suppressEvent){
14893                 this.fireEvent("selectionchange", this, this.selections);
14894             }
14895         }
14896         
14897         
14898     },
14899       /**
14900      * Unselects nodes.
14901      * @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
14902      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14903      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14904      */
14905     unselect : function(nodeInfo, keepExisting, suppressEvent)
14906     {
14907         if(nodeInfo instanceof Array){
14908             Roo.each(this.selections, function(s) {
14909                 this.unselect(s, nodeInfo);
14910             }, this);
14911             return;
14912         }
14913         var node = this.getNode(nodeInfo);
14914         if(!node || !this.isSelected(node)){
14915             //Roo.log("not selected");
14916             return; // not selected.
14917         }
14918         // fireevent???
14919         var ns = [];
14920         Roo.each(this.selections, function(s) {
14921             if (s == node ) {
14922                 Roo.fly(node).removeClass(this.selectedClass);
14923
14924                 return;
14925             }
14926             ns.push(s);
14927         },this);
14928         
14929         this.selections= ns;
14930         this.fireEvent("selectionchange", this, this.selections);
14931     },
14932
14933     /**
14934      * Gets a template node.
14935      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14936      * @return {HTMLElement} The node or null if it wasn't found
14937      */
14938     getNode : function(nodeInfo){
14939         if(typeof nodeInfo == "string"){
14940             return document.getElementById(nodeInfo);
14941         }else if(typeof nodeInfo == "number"){
14942             return this.nodes[nodeInfo];
14943         }
14944         return nodeInfo;
14945     },
14946
14947     /**
14948      * Gets a range template nodes.
14949      * @param {Number} startIndex
14950      * @param {Number} endIndex
14951      * @return {Array} An array of nodes
14952      */
14953     getNodes : function(start, end){
14954         var ns = this.nodes;
14955         start = start || 0;
14956         end = typeof end == "undefined" ? ns.length - 1 : end;
14957         var nodes = [];
14958         if(start <= end){
14959             for(var i = start; i <= end; i++){
14960                 nodes.push(ns[i]);
14961             }
14962         } else{
14963             for(var i = start; i >= end; i--){
14964                 nodes.push(ns[i]);
14965             }
14966         }
14967         return nodes;
14968     },
14969
14970     /**
14971      * Finds the index of the passed node
14972      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14973      * @return {Number} The index of the node or -1
14974      */
14975     indexOf : function(node){
14976         node = this.getNode(node);
14977         if(typeof node.nodeIndex == "number"){
14978             return node.nodeIndex;
14979         }
14980         var ns = this.nodes;
14981         for(var i = 0, len = ns.length; i < len; i++){
14982             if(ns[i] == node){
14983                 return i;
14984             }
14985         }
14986         return -1;
14987     }
14988 });
14989 /*
14990  * - LGPL
14991  *
14992  * based on jquery fullcalendar
14993  * 
14994  */
14995
14996 Roo.bootstrap = Roo.bootstrap || {};
14997 /**
14998  * @class Roo.bootstrap.Calendar
14999  * @extends Roo.bootstrap.Component
15000  * Bootstrap Calendar class
15001  * @cfg {Boolean} loadMask (true|false) default false
15002  * @cfg {Object} header generate the user specific header of the calendar, default false
15003
15004  * @constructor
15005  * Create a new Container
15006  * @param {Object} config The config object
15007  */
15008
15009
15010
15011 Roo.bootstrap.Calendar = function(config){
15012     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15013      this.addEvents({
15014         /**
15015              * @event select
15016              * Fires when a date is selected
15017              * @param {DatePicker} this
15018              * @param {Date} date The selected date
15019              */
15020         'select': true,
15021         /**
15022              * @event monthchange
15023              * Fires when the displayed month changes 
15024              * @param {DatePicker} this
15025              * @param {Date} date The selected month
15026              */
15027         'monthchange': true,
15028         /**
15029              * @event evententer
15030              * Fires when mouse over an event
15031              * @param {Calendar} this
15032              * @param {event} Event
15033              */
15034         'evententer': true,
15035         /**
15036              * @event eventleave
15037              * Fires when the mouse leaves an
15038              * @param {Calendar} this
15039              * @param {event}
15040              */
15041         'eventleave': true,
15042         /**
15043              * @event eventclick
15044              * Fires when the mouse click an
15045              * @param {Calendar} this
15046              * @param {event}
15047              */
15048         'eventclick': true
15049         
15050     });
15051
15052 };
15053
15054 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15055     
15056      /**
15057      * @cfg {Number} startDay
15058      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15059      */
15060     startDay : 0,
15061     
15062     loadMask : false,
15063     
15064     header : false,
15065       
15066     getAutoCreate : function(){
15067         
15068         
15069         var fc_button = function(name, corner, style, content ) {
15070             return Roo.apply({},{
15071                 tag : 'span',
15072                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15073                          (corner.length ?
15074                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15075                             ''
15076                         ),
15077                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15078                 unselectable: 'on'
15079             });
15080         };
15081         
15082         var header = {};
15083         
15084         if(!this.header){
15085             header = {
15086                 tag : 'table',
15087                 cls : 'fc-header',
15088                 style : 'width:100%',
15089                 cn : [
15090                     {
15091                         tag: 'tr',
15092                         cn : [
15093                             {
15094                                 tag : 'td',
15095                                 cls : 'fc-header-left',
15096                                 cn : [
15097                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15098                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15099                                     { tag: 'span', cls: 'fc-header-space' },
15100                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15101
15102
15103                                 ]
15104                             },
15105
15106                             {
15107                                 tag : 'td',
15108                                 cls : 'fc-header-center',
15109                                 cn : [
15110                                     {
15111                                         tag: 'span',
15112                                         cls: 'fc-header-title',
15113                                         cn : {
15114                                             tag: 'H2',
15115                                             html : 'month / year'
15116                                         }
15117                                     }
15118
15119                                 ]
15120                             },
15121                             {
15122                                 tag : 'td',
15123                                 cls : 'fc-header-right',
15124                                 cn : [
15125                               /*      fc_button('month', 'left', '', 'month' ),
15126                                     fc_button('week', '', '', 'week' ),
15127                                     fc_button('day', 'right', '', 'day' )
15128                                 */    
15129
15130                                 ]
15131                             }
15132
15133                         ]
15134                     }
15135                 ]
15136             };
15137         }
15138         
15139         header = this.header;
15140         
15141        
15142         var cal_heads = function() {
15143             var ret = [];
15144             // fixme - handle this.
15145             
15146             for (var i =0; i < Date.dayNames.length; i++) {
15147                 var d = Date.dayNames[i];
15148                 ret.push({
15149                     tag: 'th',
15150                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15151                     html : d.substring(0,3)
15152                 });
15153                 
15154             }
15155             ret[0].cls += ' fc-first';
15156             ret[6].cls += ' fc-last';
15157             return ret;
15158         };
15159         var cal_cell = function(n) {
15160             return  {
15161                 tag: 'td',
15162                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15163                 cn : [
15164                     {
15165                         cn : [
15166                             {
15167                                 cls: 'fc-day-number',
15168                                 html: 'D'
15169                             },
15170                             {
15171                                 cls: 'fc-day-content',
15172                              
15173                                 cn : [
15174                                      {
15175                                         style: 'position: relative;' // height: 17px;
15176                                     }
15177                                 ]
15178                             }
15179                             
15180                             
15181                         ]
15182                     }
15183                 ]
15184                 
15185             }
15186         };
15187         var cal_rows = function() {
15188             
15189             var ret = [];
15190             for (var r = 0; r < 6; r++) {
15191                 var row= {
15192                     tag : 'tr',
15193                     cls : 'fc-week',
15194                     cn : []
15195                 };
15196                 
15197                 for (var i =0; i < Date.dayNames.length; i++) {
15198                     var d = Date.dayNames[i];
15199                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15200
15201                 }
15202                 row.cn[0].cls+=' fc-first';
15203                 row.cn[0].cn[0].style = 'min-height:90px';
15204                 row.cn[6].cls+=' fc-last';
15205                 ret.push(row);
15206                 
15207             }
15208             ret[0].cls += ' fc-first';
15209             ret[4].cls += ' fc-prev-last';
15210             ret[5].cls += ' fc-last';
15211             return ret;
15212             
15213         };
15214         
15215         var cal_table = {
15216             tag: 'table',
15217             cls: 'fc-border-separate',
15218             style : 'width:100%',
15219             cellspacing  : 0,
15220             cn : [
15221                 { 
15222                     tag: 'thead',
15223                     cn : [
15224                         { 
15225                             tag: 'tr',
15226                             cls : 'fc-first fc-last',
15227                             cn : cal_heads()
15228                         }
15229                     ]
15230                 },
15231                 { 
15232                     tag: 'tbody',
15233                     cn : cal_rows()
15234                 }
15235                   
15236             ]
15237         };
15238          
15239          var cfg = {
15240             cls : 'fc fc-ltr',
15241             cn : [
15242                 header,
15243                 {
15244                     cls : 'fc-content',
15245                     style : "position: relative;",
15246                     cn : [
15247                         {
15248                             cls : 'fc-view fc-view-month fc-grid',
15249                             style : 'position: relative',
15250                             unselectable : 'on',
15251                             cn : [
15252                                 {
15253                                     cls : 'fc-event-container',
15254                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15255                                 },
15256                                 cal_table
15257                             ]
15258                         }
15259                     ]
15260     
15261                 }
15262            ] 
15263             
15264         };
15265         
15266          
15267         
15268         return cfg;
15269     },
15270     
15271     
15272     initEvents : function()
15273     {
15274         if(!this.store){
15275             throw "can not find store for calendar";
15276         }
15277         
15278         var mark = {
15279             tag: "div",
15280             cls:"x-dlg-mask",
15281             style: "text-align:center",
15282             cn: [
15283                 {
15284                     tag: "div",
15285                     style: "background-color:white;width:50%;margin:250 auto",
15286                     cn: [
15287                         {
15288                             tag: "img",
15289                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15290                         },
15291                         {
15292                             tag: "span",
15293                             html: "Loading"
15294                         }
15295                         
15296                     ]
15297                 }
15298             ]
15299         };
15300         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15301         
15302         var size = this.el.select('.fc-content', true).first().getSize();
15303         this.maskEl.setSize(size.width, size.height);
15304         this.maskEl.enableDisplayMode("block");
15305         if(!this.loadMask){
15306             this.maskEl.hide();
15307         }
15308         
15309         this.store = Roo.factory(this.store, Roo.data);
15310         this.store.on('load', this.onLoad, this);
15311         this.store.on('beforeload', this.onBeforeLoad, this);
15312         
15313         this.resize();
15314         
15315         this.cells = this.el.select('.fc-day',true);
15316         //Roo.log(this.cells);
15317         this.textNodes = this.el.query('.fc-day-number');
15318         this.cells.addClassOnOver('fc-state-hover');
15319         
15320         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15321         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15322         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15323         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15324         
15325         this.on('monthchange', this.onMonthChange, this);
15326         
15327         this.update(new Date().clearTime());
15328     },
15329     
15330     resize : function() {
15331         var sz  = this.el.getSize();
15332         
15333         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15334         this.el.select('.fc-day-content div',true).setHeight(34);
15335     },
15336     
15337     
15338     // private
15339     showPrevMonth : function(e){
15340         this.update(this.activeDate.add("mo", -1));
15341     },
15342     showToday : function(e){
15343         this.update(new Date().clearTime());
15344     },
15345     // private
15346     showNextMonth : function(e){
15347         this.update(this.activeDate.add("mo", 1));
15348     },
15349
15350     // private
15351     showPrevYear : function(){
15352         this.update(this.activeDate.add("y", -1));
15353     },
15354
15355     // private
15356     showNextYear : function(){
15357         this.update(this.activeDate.add("y", 1));
15358     },
15359
15360     
15361    // private
15362     update : function(date)
15363     {
15364         var vd = this.activeDate;
15365         this.activeDate = date;
15366 //        if(vd && this.el){
15367 //            var t = date.getTime();
15368 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15369 //                Roo.log('using add remove');
15370 //                
15371 //                this.fireEvent('monthchange', this, date);
15372 //                
15373 //                this.cells.removeClass("fc-state-highlight");
15374 //                this.cells.each(function(c){
15375 //                   if(c.dateValue == t){
15376 //                       c.addClass("fc-state-highlight");
15377 //                       setTimeout(function(){
15378 //                            try{c.dom.firstChild.focus();}catch(e){}
15379 //                       }, 50);
15380 //                       return false;
15381 //                   }
15382 //                   return true;
15383 //                });
15384 //                return;
15385 //            }
15386 //        }
15387         
15388         var days = date.getDaysInMonth();
15389         
15390         var firstOfMonth = date.getFirstDateOfMonth();
15391         var startingPos = firstOfMonth.getDay()-this.startDay;
15392         
15393         if(startingPos < this.startDay){
15394             startingPos += 7;
15395         }
15396         
15397         var pm = date.add(Date.MONTH, -1);
15398         var prevStart = pm.getDaysInMonth()-startingPos;
15399 //        
15400         this.cells = this.el.select('.fc-day',true);
15401         this.textNodes = this.el.query('.fc-day-number');
15402         this.cells.addClassOnOver('fc-state-hover');
15403         
15404         var cells = this.cells.elements;
15405         var textEls = this.textNodes;
15406         
15407         Roo.each(cells, function(cell){
15408             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15409         });
15410         
15411         days += startingPos;
15412
15413         // convert everything to numbers so it's fast
15414         var day = 86400000;
15415         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15416         //Roo.log(d);
15417         //Roo.log(pm);
15418         //Roo.log(prevStart);
15419         
15420         var today = new Date().clearTime().getTime();
15421         var sel = date.clearTime().getTime();
15422         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15423         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15424         var ddMatch = this.disabledDatesRE;
15425         var ddText = this.disabledDatesText;
15426         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15427         var ddaysText = this.disabledDaysText;
15428         var format = this.format;
15429         
15430         var setCellClass = function(cal, cell){
15431             cell.row = 0;
15432             cell.events = [];
15433             cell.more = [];
15434             //Roo.log('set Cell Class');
15435             cell.title = "";
15436             var t = d.getTime();
15437             
15438             //Roo.log(d);
15439             
15440             cell.dateValue = t;
15441             if(t == today){
15442                 cell.className += " fc-today";
15443                 cell.className += " fc-state-highlight";
15444                 cell.title = cal.todayText;
15445             }
15446             if(t == sel){
15447                 // disable highlight in other month..
15448                 //cell.className += " fc-state-highlight";
15449                 
15450             }
15451             // disabling
15452             if(t < min) {
15453                 cell.className = " fc-state-disabled";
15454                 cell.title = cal.minText;
15455                 return;
15456             }
15457             if(t > max) {
15458                 cell.className = " fc-state-disabled";
15459                 cell.title = cal.maxText;
15460                 return;
15461             }
15462             if(ddays){
15463                 if(ddays.indexOf(d.getDay()) != -1){
15464                     cell.title = ddaysText;
15465                     cell.className = " fc-state-disabled";
15466                 }
15467             }
15468             if(ddMatch && format){
15469                 var fvalue = d.dateFormat(format);
15470                 if(ddMatch.test(fvalue)){
15471                     cell.title = ddText.replace("%0", fvalue);
15472                     cell.className = " fc-state-disabled";
15473                 }
15474             }
15475             
15476             if (!cell.initialClassName) {
15477                 cell.initialClassName = cell.dom.className;
15478             }
15479             
15480             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15481         };
15482
15483         var i = 0;
15484         
15485         for(; i < startingPos; i++) {
15486             textEls[i].innerHTML = (++prevStart);
15487             d.setDate(d.getDate()+1);
15488             
15489             cells[i].className = "fc-past fc-other-month";
15490             setCellClass(this, cells[i]);
15491         }
15492         
15493         var intDay = 0;
15494         
15495         for(; i < days; i++){
15496             intDay = i - startingPos + 1;
15497             textEls[i].innerHTML = (intDay);
15498             d.setDate(d.getDate()+1);
15499             
15500             cells[i].className = ''; // "x-date-active";
15501             setCellClass(this, cells[i]);
15502         }
15503         var extraDays = 0;
15504         
15505         for(; i < 42; i++) {
15506             textEls[i].innerHTML = (++extraDays);
15507             d.setDate(d.getDate()+1);
15508             
15509             cells[i].className = "fc-future fc-other-month";
15510             setCellClass(this, cells[i]);
15511         }
15512         
15513         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15514         
15515         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15516         
15517         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15518         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15519         
15520         if(totalRows != 6){
15521             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15522             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15523         }
15524         
15525         this.fireEvent('monthchange', this, date);
15526         
15527         
15528         /*
15529         if(!this.internalRender){
15530             var main = this.el.dom.firstChild;
15531             var w = main.offsetWidth;
15532             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15533             Roo.fly(main).setWidth(w);
15534             this.internalRender = true;
15535             // opera does not respect the auto grow header center column
15536             // then, after it gets a width opera refuses to recalculate
15537             // without a second pass
15538             if(Roo.isOpera && !this.secondPass){
15539                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15540                 this.secondPass = true;
15541                 this.update.defer(10, this, [date]);
15542             }
15543         }
15544         */
15545         
15546     },
15547     
15548     findCell : function(dt) {
15549         dt = dt.clearTime().getTime();
15550         var ret = false;
15551         this.cells.each(function(c){
15552             //Roo.log("check " +c.dateValue + '?=' + dt);
15553             if(c.dateValue == dt){
15554                 ret = c;
15555                 return false;
15556             }
15557             return true;
15558         });
15559         
15560         return ret;
15561     },
15562     
15563     findCells : function(ev) {
15564         var s = ev.start.clone().clearTime().getTime();
15565        // Roo.log(s);
15566         var e= ev.end.clone().clearTime().getTime();
15567        // Roo.log(e);
15568         var ret = [];
15569         this.cells.each(function(c){
15570              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15571             
15572             if(c.dateValue > e){
15573                 return ;
15574             }
15575             if(c.dateValue < s){
15576                 return ;
15577             }
15578             ret.push(c);
15579         });
15580         
15581         return ret;    
15582     },
15583     
15584 //    findBestRow: function(cells)
15585 //    {
15586 //        var ret = 0;
15587 //        
15588 //        for (var i =0 ; i < cells.length;i++) {
15589 //            ret  = Math.max(cells[i].rows || 0,ret);
15590 //        }
15591 //        return ret;
15592 //        
15593 //    },
15594     
15595     
15596     addItem : function(ev)
15597     {
15598         // look for vertical location slot in
15599         var cells = this.findCells(ev);
15600         
15601 //        ev.row = this.findBestRow(cells);
15602         
15603         // work out the location.
15604         
15605         var crow = false;
15606         var rows = [];
15607         for(var i =0; i < cells.length; i++) {
15608             
15609             cells[i].row = cells[0].row;
15610             
15611             if(i == 0){
15612                 cells[i].row = cells[i].row + 1;
15613             }
15614             
15615             if (!crow) {
15616                 crow = {
15617                     start : cells[i],
15618                     end :  cells[i]
15619                 };
15620                 continue;
15621             }
15622             if (crow.start.getY() == cells[i].getY()) {
15623                 // on same row.
15624                 crow.end = cells[i];
15625                 continue;
15626             }
15627             // different row.
15628             rows.push(crow);
15629             crow = {
15630                 start: cells[i],
15631                 end : cells[i]
15632             };
15633             
15634         }
15635         
15636         rows.push(crow);
15637         ev.els = [];
15638         ev.rows = rows;
15639         ev.cells = cells;
15640         
15641         cells[0].events.push(ev);
15642         
15643         this.calevents.push(ev);
15644     },
15645     
15646     clearEvents: function() {
15647         
15648         if(!this.calevents){
15649             return;
15650         }
15651         
15652         Roo.each(this.cells.elements, function(c){
15653             c.row = 0;
15654             c.events = [];
15655             c.more = [];
15656         });
15657         
15658         Roo.each(this.calevents, function(e) {
15659             Roo.each(e.els, function(el) {
15660                 el.un('mouseenter' ,this.onEventEnter, this);
15661                 el.un('mouseleave' ,this.onEventLeave, this);
15662                 el.remove();
15663             },this);
15664         },this);
15665         
15666         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15667             e.remove();
15668         });
15669         
15670     },
15671     
15672     renderEvents: function()
15673     {   
15674         var _this = this;
15675         
15676         this.cells.each(function(c) {
15677             
15678             if(c.row < 5){
15679                 return;
15680             }
15681             
15682             var ev = c.events;
15683             
15684             var r = 4;
15685             if(c.row != c.events.length){
15686                 r = 4 - (4 - (c.row - c.events.length));
15687             }
15688             
15689             c.events = ev.slice(0, r);
15690             c.more = ev.slice(r);
15691             
15692             if(c.more.length && c.more.length == 1){
15693                 c.events.push(c.more.pop());
15694             }
15695             
15696             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15697             
15698         });
15699             
15700         this.cells.each(function(c) {
15701             
15702             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15703             
15704             
15705             for (var e = 0; e < c.events.length; e++){
15706                 var ev = c.events[e];
15707                 var rows = ev.rows;
15708                 
15709                 for(var i = 0; i < rows.length; i++) {
15710                 
15711                     // how many rows should it span..
15712
15713                     var  cfg = {
15714                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15715                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15716
15717                         unselectable : "on",
15718                         cn : [
15719                             {
15720                                 cls: 'fc-event-inner',
15721                                 cn : [
15722     //                                {
15723     //                                  tag:'span',
15724     //                                  cls: 'fc-event-time',
15725     //                                  html : cells.length > 1 ? '' : ev.time
15726     //                                },
15727                                     {
15728                                       tag:'span',
15729                                       cls: 'fc-event-title',
15730                                       html : String.format('{0}', ev.title)
15731                                     }
15732
15733
15734                                 ]
15735                             },
15736                             {
15737                                 cls: 'ui-resizable-handle ui-resizable-e',
15738                                 html : '&nbsp;&nbsp;&nbsp'
15739                             }
15740
15741                         ]
15742                     };
15743
15744                     if (i == 0) {
15745                         cfg.cls += ' fc-event-start';
15746                     }
15747                     if ((i+1) == rows.length) {
15748                         cfg.cls += ' fc-event-end';
15749                     }
15750
15751                     var ctr = _this.el.select('.fc-event-container',true).first();
15752                     var cg = ctr.createChild(cfg);
15753
15754                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15755                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15756
15757                     var r = (c.more.length) ? 1 : 0;
15758                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15759                     cg.setWidth(ebox.right - sbox.x -2);
15760
15761                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15762                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15763                     cg.on('click', _this.onEventClick, _this, ev);
15764
15765                     ev.els.push(cg);
15766                     
15767                 }
15768                 
15769             }
15770             
15771             
15772             if(c.more.length){
15773                 var  cfg = {
15774                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15775                     style : 'position: absolute',
15776                     unselectable : "on",
15777                     cn : [
15778                         {
15779                             cls: 'fc-event-inner',
15780                             cn : [
15781                                 {
15782                                   tag:'span',
15783                                   cls: 'fc-event-title',
15784                                   html : 'More'
15785                                 }
15786
15787
15788                             ]
15789                         },
15790                         {
15791                             cls: 'ui-resizable-handle ui-resizable-e',
15792                             html : '&nbsp;&nbsp;&nbsp'
15793                         }
15794
15795                     ]
15796                 };
15797
15798                 var ctr = _this.el.select('.fc-event-container',true).first();
15799                 var cg = ctr.createChild(cfg);
15800
15801                 var sbox = c.select('.fc-day-content',true).first().getBox();
15802                 var ebox = c.select('.fc-day-content',true).first().getBox();
15803                 //Roo.log(cg);
15804                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15805                 cg.setWidth(ebox.right - sbox.x -2);
15806
15807                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15808                 
15809             }
15810             
15811         });
15812         
15813         
15814         
15815     },
15816     
15817     onEventEnter: function (e, el,event,d) {
15818         this.fireEvent('evententer', this, el, event);
15819     },
15820     
15821     onEventLeave: function (e, el,event,d) {
15822         this.fireEvent('eventleave', this, el, event);
15823     },
15824     
15825     onEventClick: function (e, el,event,d) {
15826         this.fireEvent('eventclick', this, el, event);
15827     },
15828     
15829     onMonthChange: function () {
15830         this.store.load();
15831     },
15832     
15833     onMoreEventClick: function(e, el, more)
15834     {
15835         var _this = this;
15836         
15837         this.calpopover.placement = 'right';
15838         this.calpopover.setTitle('More');
15839         
15840         this.calpopover.setContent('');
15841         
15842         var ctr = this.calpopover.el.select('.popover-content', true).first();
15843         
15844         Roo.each(more, function(m){
15845             var cfg = {
15846                 cls : 'fc-event-hori fc-event-draggable',
15847                 html : m.title
15848             };
15849             var cg = ctr.createChild(cfg);
15850             
15851             cg.on('click', _this.onEventClick, _this, m);
15852         });
15853         
15854         this.calpopover.show(el);
15855         
15856         
15857     },
15858     
15859     onLoad: function () 
15860     {   
15861         this.calevents = [];
15862         var cal = this;
15863         
15864         if(this.store.getCount() > 0){
15865             this.store.data.each(function(d){
15866                cal.addItem({
15867                     id : d.data.id,
15868                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15869                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15870                     time : d.data.start_time,
15871                     title : d.data.title,
15872                     description : d.data.description,
15873                     venue : d.data.venue
15874                 });
15875             });
15876         }
15877         
15878         this.renderEvents();
15879         
15880         if(this.calevents.length && this.loadMask){
15881             this.maskEl.hide();
15882         }
15883     },
15884     
15885     onBeforeLoad: function()
15886     {
15887         this.clearEvents();
15888         if(this.loadMask){
15889             this.maskEl.show();
15890         }
15891     }
15892 });
15893
15894  
15895  /*
15896  * - LGPL
15897  *
15898  * element
15899  * 
15900  */
15901
15902 /**
15903  * @class Roo.bootstrap.Popover
15904  * @extends Roo.bootstrap.Component
15905  * Bootstrap Popover class
15906  * @cfg {String} html contents of the popover   (or false to use children..)
15907  * @cfg {String} title of popover (or false to hide)
15908  * @cfg {String} placement how it is placed
15909  * @cfg {String} trigger click || hover (or false to trigger manually)
15910  * @cfg {String} over what (parent or false to trigger manually.)
15911  * @cfg {Number} delay - delay before showing
15912  
15913  * @constructor
15914  * Create a new Popover
15915  * @param {Object} config The config object
15916  */
15917
15918 Roo.bootstrap.Popover = function(config){
15919     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15920     
15921     this.addEvents({
15922         // raw events
15923          /**
15924          * @event show
15925          * After the popover show
15926          * 
15927          * @param {Roo.bootstrap.Popover} this
15928          */
15929         "show" : true,
15930         /**
15931          * @event hide
15932          * After the popover hide
15933          * 
15934          * @param {Roo.bootstrap.Popover} this
15935          */
15936         "hide" : true
15937     });
15938 };
15939
15940 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15941     
15942     title: 'Fill in a title',
15943     html: false,
15944     
15945     placement : 'right',
15946     trigger : 'hover', // hover
15947     
15948     delay : 0,
15949     
15950     over: 'parent',
15951     
15952     can_build_overlaid : false,
15953     
15954     getChildContainer : function()
15955     {
15956         return this.el.select('.popover-content',true).first();
15957     },
15958     
15959     getAutoCreate : function(){
15960          
15961         var cfg = {
15962            cls : 'popover roo-dynamic',
15963            style: 'display:block',
15964            cn : [
15965                 {
15966                     cls : 'arrow'
15967                 },
15968                 {
15969                     cls : 'popover-inner',
15970                     cn : [
15971                         {
15972                             tag: 'h3',
15973                             cls: 'popover-title',
15974                             html : this.title
15975                         },
15976                         {
15977                             cls : 'popover-content',
15978                             html : this.html
15979                         }
15980                     ]
15981                     
15982                 }
15983            ]
15984         };
15985         
15986         return cfg;
15987     },
15988     setTitle: function(str)
15989     {
15990         this.title = str;
15991         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15992     },
15993     setContent: function(str)
15994     {
15995         this.html = str;
15996         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15997     },
15998     // as it get's added to the bottom of the page.
15999     onRender : function(ct, position)
16000     {
16001         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16002         if(!this.el){
16003             var cfg = Roo.apply({},  this.getAutoCreate());
16004             cfg.id = Roo.id();
16005             
16006             if (this.cls) {
16007                 cfg.cls += ' ' + this.cls;
16008             }
16009             if (this.style) {
16010                 cfg.style = this.style;
16011             }
16012             //Roo.log("adding to ");
16013             this.el = Roo.get(document.body).createChild(cfg, position);
16014 //            Roo.log(this.el);
16015         }
16016         this.initEvents();
16017     },
16018     
16019     initEvents : function()
16020     {
16021         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16022         this.el.enableDisplayMode('block');
16023         this.el.hide();
16024         if (this.over === false) {
16025             return; 
16026         }
16027         if (this.triggers === false) {
16028             return;
16029         }
16030         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16031         var triggers = this.trigger ? this.trigger.split(' ') : [];
16032         Roo.each(triggers, function(trigger) {
16033         
16034             if (trigger == 'click') {
16035                 on_el.on('click', this.toggle, this);
16036             } else if (trigger != 'manual') {
16037                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16038                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16039       
16040                 on_el.on(eventIn  ,this.enter, this);
16041                 on_el.on(eventOut, this.leave, this);
16042             }
16043         }, this);
16044         
16045     },
16046     
16047     
16048     // private
16049     timeout : null,
16050     hoverState : null,
16051     
16052     toggle : function () {
16053         this.hoverState == 'in' ? this.leave() : this.enter();
16054     },
16055     
16056     enter : function () {
16057         
16058         clearTimeout(this.timeout);
16059     
16060         this.hoverState = 'in';
16061     
16062         if (!this.delay || !this.delay.show) {
16063             this.show();
16064             return;
16065         }
16066         var _t = this;
16067         this.timeout = setTimeout(function () {
16068             if (_t.hoverState == 'in') {
16069                 _t.show();
16070             }
16071         }, this.delay.show)
16072     },
16073     
16074     leave : function() {
16075         clearTimeout(this.timeout);
16076     
16077         this.hoverState = 'out';
16078     
16079         if (!this.delay || !this.delay.hide) {
16080             this.hide();
16081             return;
16082         }
16083         var _t = this;
16084         this.timeout = setTimeout(function () {
16085             if (_t.hoverState == 'out') {
16086                 _t.hide();
16087             }
16088         }, this.delay.hide)
16089     },
16090     
16091     show : function (on_el)
16092     {
16093         if (!on_el) {
16094             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16095         }
16096         
16097         // set content.
16098         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16099         if (this.html !== false) {
16100             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16101         }
16102         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16103         if (!this.title.length) {
16104             this.el.select('.popover-title',true).hide();
16105         }
16106         
16107         var placement = typeof this.placement == 'function' ?
16108             this.placement.call(this, this.el, on_el) :
16109             this.placement;
16110             
16111         var autoToken = /\s?auto?\s?/i;
16112         var autoPlace = autoToken.test(placement);
16113         if (autoPlace) {
16114             placement = placement.replace(autoToken, '') || 'top';
16115         }
16116         
16117         //this.el.detach()
16118         //this.el.setXY([0,0]);
16119         this.el.show();
16120         this.el.dom.style.display='block';
16121         this.el.addClass(placement);
16122         
16123         //this.el.appendTo(on_el);
16124         
16125         var p = this.getPosition();
16126         var box = this.el.getBox();
16127         
16128         if (autoPlace) {
16129             // fixme..
16130         }
16131         var align = Roo.bootstrap.Popover.alignment[placement];
16132         this.el.alignTo(on_el, align[0],align[1]);
16133         //var arrow = this.el.select('.arrow',true).first();
16134         //arrow.set(align[2], 
16135         
16136         this.el.addClass('in');
16137         
16138         
16139         if (this.el.hasClass('fade')) {
16140             // fade it?
16141         }
16142         
16143         this.hoverState = 'in';
16144         
16145         this.fireEvent('show', this);
16146         
16147     },
16148     hide : function()
16149     {
16150         this.el.setXY([0,0]);
16151         this.el.removeClass('in');
16152         this.el.hide();
16153         this.hoverState = null;
16154         
16155         this.fireEvent('hide', this);
16156     }
16157     
16158 });
16159
16160 Roo.bootstrap.Popover.alignment = {
16161     'left' : ['r-l', [-10,0], 'right'],
16162     'right' : ['l-r', [10,0], 'left'],
16163     'bottom' : ['t-b', [0,10], 'top'],
16164     'top' : [ 'b-t', [0,-10], 'bottom']
16165 };
16166
16167  /*
16168  * - LGPL
16169  *
16170  * Progress
16171  * 
16172  */
16173
16174 /**
16175  * @class Roo.bootstrap.Progress
16176  * @extends Roo.bootstrap.Component
16177  * Bootstrap Progress class
16178  * @cfg {Boolean} striped striped of the progress bar
16179  * @cfg {Boolean} active animated of the progress bar
16180  * 
16181  * 
16182  * @constructor
16183  * Create a new Progress
16184  * @param {Object} config The config object
16185  */
16186
16187 Roo.bootstrap.Progress = function(config){
16188     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16189 };
16190
16191 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16192     
16193     striped : false,
16194     active: false,
16195     
16196     getAutoCreate : function(){
16197         var cfg = {
16198             tag: 'div',
16199             cls: 'progress'
16200         };
16201         
16202         
16203         if(this.striped){
16204             cfg.cls += ' progress-striped';
16205         }
16206       
16207         if(this.active){
16208             cfg.cls += ' active';
16209         }
16210         
16211         
16212         return cfg;
16213     }
16214    
16215 });
16216
16217  
16218
16219  /*
16220  * - LGPL
16221  *
16222  * ProgressBar
16223  * 
16224  */
16225
16226 /**
16227  * @class Roo.bootstrap.ProgressBar
16228  * @extends Roo.bootstrap.Component
16229  * Bootstrap ProgressBar class
16230  * @cfg {Number} aria_valuenow aria-value now
16231  * @cfg {Number} aria_valuemin aria-value min
16232  * @cfg {Number} aria_valuemax aria-value max
16233  * @cfg {String} label label for the progress bar
16234  * @cfg {String} panel (success | info | warning | danger )
16235  * @cfg {String} role role of the progress bar
16236  * @cfg {String} sr_only text
16237  * 
16238  * 
16239  * @constructor
16240  * Create a new ProgressBar
16241  * @param {Object} config The config object
16242  */
16243
16244 Roo.bootstrap.ProgressBar = function(config){
16245     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16246 };
16247
16248 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16249     
16250     aria_valuenow : 0,
16251     aria_valuemin : 0,
16252     aria_valuemax : 100,
16253     label : false,
16254     panel : false,
16255     role : false,
16256     sr_only: false,
16257     
16258     getAutoCreate : function()
16259     {
16260         
16261         var cfg = {
16262             tag: 'div',
16263             cls: 'progress-bar',
16264             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16265         };
16266         
16267         if(this.sr_only){
16268             cfg.cn = {
16269                 tag: 'span',
16270                 cls: 'sr-only',
16271                 html: this.sr_only
16272             }
16273         }
16274         
16275         if(this.role){
16276             cfg.role = this.role;
16277         }
16278         
16279         if(this.aria_valuenow){
16280             cfg['aria-valuenow'] = this.aria_valuenow;
16281         }
16282         
16283         if(this.aria_valuemin){
16284             cfg['aria-valuemin'] = this.aria_valuemin;
16285         }
16286         
16287         if(this.aria_valuemax){
16288             cfg['aria-valuemax'] = this.aria_valuemax;
16289         }
16290         
16291         if(this.label && !this.sr_only){
16292             cfg.html = this.label;
16293         }
16294         
16295         if(this.panel){
16296             cfg.cls += ' progress-bar-' + this.panel;
16297         }
16298         
16299         return cfg;
16300     },
16301     
16302     update : function(aria_valuenow)
16303     {
16304         this.aria_valuenow = aria_valuenow;
16305         
16306         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16307     }
16308    
16309 });
16310
16311  
16312
16313  /*
16314  * - LGPL
16315  *
16316  * column
16317  * 
16318  */
16319
16320 /**
16321  * @class Roo.bootstrap.TabGroup
16322  * @extends Roo.bootstrap.Column
16323  * Bootstrap Column class
16324  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16325  * @cfg {Boolean} carousel true to make the group behave like a carousel
16326  * @cfg {Boolean} bullets show bullets for the panels
16327  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16328  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16329  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16330  * 
16331  * @constructor
16332  * Create a new TabGroup
16333  * @param {Object} config The config object
16334  */
16335
16336 Roo.bootstrap.TabGroup = function(config){
16337     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16338     if (!this.navId) {
16339         this.navId = Roo.id();
16340     }
16341     this.tabs = [];
16342     Roo.bootstrap.TabGroup.register(this);
16343     
16344 };
16345
16346 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16347     
16348     carousel : false,
16349     transition : false,
16350     bullets : 0,
16351     timer : 0,
16352     autoslide : false,
16353     slideFn : false,
16354     slideOnTouch : false,
16355     
16356     getAutoCreate : function()
16357     {
16358         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16359         
16360         cfg.cls += ' tab-content';
16361         
16362         if (this.carousel) {
16363             cfg.cls += ' carousel slide';
16364             
16365             cfg.cn = [{
16366                cls : 'carousel-inner'
16367             }];
16368         
16369             if(this.bullets  && !Roo.isTouch){
16370                 
16371                 var bullets = {
16372                     cls : 'carousel-bullets',
16373                     cn : []
16374                 };
16375                
16376                 if(this.bullets_cls){
16377                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16378                 }
16379                  /*
16380                 for (var i = 0; i < this.bullets; i++){
16381                     bullets.cn.push({
16382                         cls : 'bullet bullet-' + i
16383                     });
16384                 }
16385                 */
16386                 bullets.cn.push({
16387                     cls : 'clear'
16388                 });
16389                 
16390                 cfg.cn[0].cn = bullets;
16391             }
16392         }
16393         
16394         return cfg;
16395     },
16396     
16397     initEvents:  function()
16398     {
16399         if(Roo.isTouch && this.slideOnTouch){
16400             this.el.on("touchstart", this.onTouchStart, this);
16401         }
16402         
16403         if(this.autoslide){
16404             var _this = this;
16405             
16406             this.slideFn = window.setInterval(function() {
16407                 _this.showPanelNext();
16408             }, this.timer);
16409         }
16410         
16411     },
16412     
16413     onTouchStart : function(e, el, o)
16414     {
16415         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16416             return;
16417         }
16418         
16419         this.showPanelNext();
16420     },
16421     
16422     getChildContainer : function()
16423     {
16424         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16425     },
16426     
16427     /**
16428     * register a Navigation item
16429     * @param {Roo.bootstrap.NavItem} the navitem to add
16430     */
16431     register : function(item)
16432     {
16433         this.tabs.push( item);
16434         item.navId = this.navId; // not really needed..
16435         this.addBullet();
16436     
16437     },
16438     
16439     getActivePanel : function()
16440     {
16441         var r = false;
16442         Roo.each(this.tabs, function(t) {
16443             if (t.active) {
16444                 r = t;
16445                 return false;
16446             }
16447             return null;
16448         });
16449         return r;
16450         
16451     },
16452     getPanelByName : function(n)
16453     {
16454         var r = false;
16455         Roo.each(this.tabs, function(t) {
16456             if (t.tabId == n) {
16457                 r = t;
16458                 return false;
16459             }
16460             return null;
16461         });
16462         return r;
16463     },
16464     indexOfPanel : function(p)
16465     {
16466         var r = false;
16467         Roo.each(this.tabs, function(t,i) {
16468             if (t.tabId == p.tabId) {
16469                 r = i;
16470                 return false;
16471             }
16472             return null;
16473         });
16474         return r;
16475     },
16476     /**
16477      * show a specific panel
16478      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16479      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16480      */
16481     showPanel : function (pan)
16482     {
16483         if(this.transition || typeof(pan) == 'undefined'){
16484             Roo.log("waiting for the transitionend");
16485             return;
16486         }
16487         
16488         if (typeof(pan) == 'number') {
16489             pan = this.tabs[pan];
16490         }
16491         
16492         if (typeof(pan) == 'string') {
16493             pan = this.getPanelByName(pan);
16494         }
16495         
16496         var cur = this.getActivePanel();
16497         
16498         if(!pan || !cur){
16499             Roo.log('pan or acitve pan is undefined');
16500             return false;
16501         }
16502         
16503         if (pan.tabId == this.getActivePanel().tabId) {
16504             return true;
16505         }
16506         
16507         if (false === cur.fireEvent('beforedeactivate')) {
16508             return false;
16509         }
16510         
16511         if(this.bullets > 0 && !Roo.isTouch){
16512             this.setActiveBullet(this.indexOfPanel(pan));
16513         }
16514         
16515         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16516             
16517             this.transition = true;
16518             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16519             var lr = dir == 'next' ? 'left' : 'right';
16520             pan.el.addClass(dir); // or prev
16521             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16522             cur.el.addClass(lr); // or right
16523             pan.el.addClass(lr);
16524             
16525             var _this = this;
16526             cur.el.on('transitionend', function() {
16527                 Roo.log("trans end?");
16528                 
16529                 pan.el.removeClass([lr,dir]);
16530                 pan.setActive(true);
16531                 
16532                 cur.el.removeClass([lr]);
16533                 cur.setActive(false);
16534                 
16535                 _this.transition = false;
16536                 
16537             }, this, { single:  true } );
16538             
16539             return true;
16540         }
16541         
16542         cur.setActive(false);
16543         pan.setActive(true);
16544         
16545         return true;
16546         
16547     },
16548     showPanelNext : function()
16549     {
16550         var i = this.indexOfPanel(this.getActivePanel());
16551         
16552         if (i >= this.tabs.length - 1 && !this.autoslide) {
16553             return;
16554         }
16555         
16556         if (i >= this.tabs.length - 1 && this.autoslide) {
16557             i = -1;
16558         }
16559         
16560         this.showPanel(this.tabs[i+1]);
16561     },
16562     
16563     showPanelPrev : function()
16564     {
16565         var i = this.indexOfPanel(this.getActivePanel());
16566         
16567         if (i  < 1 && !this.autoslide) {
16568             return;
16569         }
16570         
16571         if (i < 1 && this.autoslide) {
16572             i = this.tabs.length;
16573         }
16574         
16575         this.showPanel(this.tabs[i-1]);
16576     },
16577     
16578     
16579     addBullet: function()
16580     {
16581         if(!this.bullets || Roo.isTouch){
16582             return;
16583         }
16584         var ctr = this.el.select('.carousel-bullets',true).first();
16585         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16586         var bullet = ctr.createChild({
16587             cls : 'bullet bullet-' + i
16588         },ctr.dom.lastChild);
16589         
16590         
16591         var _this = this;
16592         
16593         bullet.on('click', (function(e, el, o, ii, t){
16594
16595             e.preventDefault();
16596
16597             this.showPanel(ii);
16598
16599             if(this.autoslide && this.slideFn){
16600                 clearInterval(this.slideFn);
16601                 this.slideFn = window.setInterval(function() {
16602                     _this.showPanelNext();
16603                 }, this.timer);
16604             }
16605
16606         }).createDelegate(this, [i, bullet], true));
16607                 
16608         
16609     },
16610      
16611     setActiveBullet : function(i)
16612     {
16613         if(Roo.isTouch){
16614             return;
16615         }
16616         
16617         Roo.each(this.el.select('.bullet', true).elements, function(el){
16618             el.removeClass('selected');
16619         });
16620
16621         var bullet = this.el.select('.bullet-' + i, true).first();
16622         
16623         if(!bullet){
16624             return;
16625         }
16626         
16627         bullet.addClass('selected');
16628     }
16629     
16630     
16631   
16632 });
16633
16634  
16635
16636  
16637  
16638 Roo.apply(Roo.bootstrap.TabGroup, {
16639     
16640     groups: {},
16641      /**
16642     * register a Navigation Group
16643     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16644     */
16645     register : function(navgrp)
16646     {
16647         this.groups[navgrp.navId] = navgrp;
16648         
16649     },
16650     /**
16651     * fetch a Navigation Group based on the navigation ID
16652     * if one does not exist , it will get created.
16653     * @param {string} the navgroup to add
16654     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16655     */
16656     get: function(navId) {
16657         if (typeof(this.groups[navId]) == 'undefined') {
16658             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16659         }
16660         return this.groups[navId] ;
16661     }
16662     
16663     
16664     
16665 });
16666
16667  /*
16668  * - LGPL
16669  *
16670  * TabPanel
16671  * 
16672  */
16673
16674 /**
16675  * @class Roo.bootstrap.TabPanel
16676  * @extends Roo.bootstrap.Component
16677  * Bootstrap TabPanel class
16678  * @cfg {Boolean} active panel active
16679  * @cfg {String} html panel content
16680  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16681  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16682  * 
16683  * 
16684  * @constructor
16685  * Create a new TabPanel
16686  * @param {Object} config The config object
16687  */
16688
16689 Roo.bootstrap.TabPanel = function(config){
16690     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16691     this.addEvents({
16692         /**
16693              * @event changed
16694              * Fires when the active status changes
16695              * @param {Roo.bootstrap.TabPanel} this
16696              * @param {Boolean} state the new state
16697             
16698          */
16699         'changed': true,
16700         /**
16701              * @event beforedeactivate
16702              * Fires before a tab is de-activated - can be used to do validation on a form.
16703              * @param {Roo.bootstrap.TabPanel} this
16704              * @return {Boolean} false if there is an error
16705             
16706          */
16707         'beforedeactivate': true
16708      });
16709     
16710     this.tabId = this.tabId || Roo.id();
16711   
16712 };
16713
16714 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16715     
16716     active: false,
16717     html: false,
16718     tabId: false,
16719     navId : false,
16720     
16721     getAutoCreate : function(){
16722         var cfg = {
16723             tag: 'div',
16724             // item is needed for carousel - not sure if it has any effect otherwise
16725             cls: 'tab-pane item',
16726             html: this.html || ''
16727         };
16728         
16729         if(this.active){
16730             cfg.cls += ' active';
16731         }
16732         
16733         if(this.tabId){
16734             cfg.tabId = this.tabId;
16735         }
16736         
16737         
16738         return cfg;
16739     },
16740     
16741     initEvents:  function()
16742     {
16743         var p = this.parent();
16744         this.navId = this.navId || p.navId;
16745         
16746         if (typeof(this.navId) != 'undefined') {
16747             // not really needed.. but just in case.. parent should be a NavGroup.
16748             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16749             
16750             tg.register(this);
16751             
16752             var i = tg.tabs.length - 1;
16753             
16754             if(this.active && tg.bullets > 0 && i < tg.bullets){
16755                 tg.setActiveBullet(i);
16756             }
16757         }
16758         
16759     },
16760     
16761     
16762     onRender : function(ct, position)
16763     {
16764        // Roo.log("Call onRender: " + this.xtype);
16765         
16766         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16767         
16768         
16769         
16770         
16771         
16772     },
16773     
16774     setActive: function(state)
16775     {
16776         Roo.log("panel - set active " + this.tabId + "=" + state);
16777         
16778         this.active = state;
16779         if (!state) {
16780             this.el.removeClass('active');
16781             
16782         } else  if (!this.el.hasClass('active')) {
16783             this.el.addClass('active');
16784         }
16785         
16786         this.fireEvent('changed', this, state);
16787     }
16788     
16789     
16790 });
16791  
16792
16793  
16794
16795  /*
16796  * - LGPL
16797  *
16798  * DateField
16799  * 
16800  */
16801
16802 /**
16803  * @class Roo.bootstrap.DateField
16804  * @extends Roo.bootstrap.Input
16805  * Bootstrap DateField class
16806  * @cfg {Number} weekStart default 0
16807  * @cfg {String} viewMode default empty, (months|years)
16808  * @cfg {String} minViewMode default empty, (months|years)
16809  * @cfg {Number} startDate default -Infinity
16810  * @cfg {Number} endDate default Infinity
16811  * @cfg {Boolean} todayHighlight default false
16812  * @cfg {Boolean} todayBtn default false
16813  * @cfg {Boolean} calendarWeeks default false
16814  * @cfg {Object} daysOfWeekDisabled default empty
16815  * @cfg {Boolean} singleMode default false (true | false)
16816  * 
16817  * @cfg {Boolean} keyboardNavigation default true
16818  * @cfg {String} language default en
16819  * 
16820  * @constructor
16821  * Create a new DateField
16822  * @param {Object} config The config object
16823  */
16824
16825 Roo.bootstrap.DateField = function(config){
16826     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16827      this.addEvents({
16828             /**
16829              * @event show
16830              * Fires when this field show.
16831              * @param {Roo.bootstrap.DateField} this
16832              * @param {Mixed} date The date value
16833              */
16834             show : true,
16835             /**
16836              * @event show
16837              * Fires when this field hide.
16838              * @param {Roo.bootstrap.DateField} this
16839              * @param {Mixed} date The date value
16840              */
16841             hide : true,
16842             /**
16843              * @event select
16844              * Fires when select a date.
16845              * @param {Roo.bootstrap.DateField} this
16846              * @param {Mixed} date The date value
16847              */
16848             select : true,
16849             /**
16850              * @event beforeselect
16851              * Fires when before select a date.
16852              * @param {Roo.bootstrap.DateField} this
16853              * @param {Mixed} date The date value
16854              */
16855             beforeselect : true
16856         });
16857 };
16858
16859 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16860     
16861     /**
16862      * @cfg {String} format
16863      * The default date format string which can be overriden for localization support.  The format must be
16864      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16865      */
16866     format : "m/d/y",
16867     /**
16868      * @cfg {String} altFormats
16869      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16870      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16871      */
16872     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16873     
16874     weekStart : 0,
16875     
16876     viewMode : '',
16877     
16878     minViewMode : '',
16879     
16880     todayHighlight : false,
16881     
16882     todayBtn: false,
16883     
16884     language: 'en',
16885     
16886     keyboardNavigation: true,
16887     
16888     calendarWeeks: false,
16889     
16890     startDate: -Infinity,
16891     
16892     endDate: Infinity,
16893     
16894     daysOfWeekDisabled: [],
16895     
16896     _events: [],
16897     
16898     singleMode : false,
16899     
16900     UTCDate: function()
16901     {
16902         return new Date(Date.UTC.apply(Date, arguments));
16903     },
16904     
16905     UTCToday: function()
16906     {
16907         var today = new Date();
16908         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16909     },
16910     
16911     getDate: function() {
16912             var d = this.getUTCDate();
16913             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16914     },
16915     
16916     getUTCDate: function() {
16917             return this.date;
16918     },
16919     
16920     setDate: function(d) {
16921             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16922     },
16923     
16924     setUTCDate: function(d) {
16925             this.date = d;
16926             this.setValue(this.formatDate(this.date));
16927     },
16928         
16929     onRender: function(ct, position)
16930     {
16931         
16932         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16933         
16934         this.language = this.language || 'en';
16935         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16936         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16937         
16938         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16939         this.format = this.format || 'm/d/y';
16940         this.isInline = false;
16941         this.isInput = true;
16942         this.component = this.el.select('.add-on', true).first() || false;
16943         this.component = (this.component && this.component.length === 0) ? false : this.component;
16944         this.hasInput = this.component && this.inputEL().length;
16945         
16946         if (typeof(this.minViewMode === 'string')) {
16947             switch (this.minViewMode) {
16948                 case 'months':
16949                     this.minViewMode = 1;
16950                     break;
16951                 case 'years':
16952                     this.minViewMode = 2;
16953                     break;
16954                 default:
16955                     this.minViewMode = 0;
16956                     break;
16957             }
16958         }
16959         
16960         if (typeof(this.viewMode === 'string')) {
16961             switch (this.viewMode) {
16962                 case 'months':
16963                     this.viewMode = 1;
16964                     break;
16965                 case 'years':
16966                     this.viewMode = 2;
16967                     break;
16968                 default:
16969                     this.viewMode = 0;
16970                     break;
16971             }
16972         }
16973                 
16974         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16975         
16976 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16977         
16978         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16979         
16980         this.picker().on('mousedown', this.onMousedown, this);
16981         this.picker().on('click', this.onClick, this);
16982         
16983         this.picker().addClass('datepicker-dropdown');
16984         
16985         this.startViewMode = this.viewMode;
16986         
16987         if(this.singleMode){
16988             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16989                 v.setVisibilityMode(Roo.Element.DISPLAY);
16990                 v.hide();
16991             });
16992             
16993             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16994                 v.setStyle('width', '189px');
16995             });
16996         }
16997         
16998         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16999             if(!this.calendarWeeks){
17000                 v.remove();
17001                 return;
17002             }
17003             
17004             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17005             v.attr('colspan', function(i, val){
17006                 return parseInt(val) + 1;
17007             });
17008         });
17009                         
17010         
17011         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17012         
17013         this.setStartDate(this.startDate);
17014         this.setEndDate(this.endDate);
17015         
17016         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17017         
17018         this.fillDow();
17019         this.fillMonths();
17020         this.update();
17021         this.showMode();
17022         
17023         if(this.isInline) {
17024             this.show();
17025         }
17026     },
17027     
17028     picker : function()
17029     {
17030         return this.pickerEl;
17031 //        return this.el.select('.datepicker', true).first();
17032     },
17033     
17034     fillDow: function()
17035     {
17036         var dowCnt = this.weekStart;
17037         
17038         var dow = {
17039             tag: 'tr',
17040             cn: [
17041                 
17042             ]
17043         };
17044         
17045         if(this.calendarWeeks){
17046             dow.cn.push({
17047                 tag: 'th',
17048                 cls: 'cw',
17049                 html: '&nbsp;'
17050             })
17051         }
17052         
17053         while (dowCnt < this.weekStart + 7) {
17054             dow.cn.push({
17055                 tag: 'th',
17056                 cls: 'dow',
17057                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17058             });
17059         }
17060         
17061         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17062     },
17063     
17064     fillMonths: function()
17065     {    
17066         var i = 0;
17067         var months = this.picker().select('>.datepicker-months td', true).first();
17068         
17069         months.dom.innerHTML = '';
17070         
17071         while (i < 12) {
17072             var month = {
17073                 tag: 'span',
17074                 cls: 'month',
17075                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17076             };
17077             
17078             months.createChild(month);
17079         }
17080         
17081     },
17082     
17083     update: function()
17084     {
17085         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;
17086         
17087         if (this.date < this.startDate) {
17088             this.viewDate = new Date(this.startDate);
17089         } else if (this.date > this.endDate) {
17090             this.viewDate = new Date(this.endDate);
17091         } else {
17092             this.viewDate = new Date(this.date);
17093         }
17094         
17095         this.fill();
17096     },
17097     
17098     fill: function() 
17099     {
17100         var d = new Date(this.viewDate),
17101                 year = d.getUTCFullYear(),
17102                 month = d.getUTCMonth(),
17103                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17104                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17105                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17106                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17107                 currentDate = this.date && this.date.valueOf(),
17108                 today = this.UTCToday();
17109         
17110         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17111         
17112 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17113         
17114 //        this.picker.select('>tfoot th.today').
17115 //                                              .text(dates[this.language].today)
17116 //                                              .toggle(this.todayBtn !== false);
17117     
17118         this.updateNavArrows();
17119         this.fillMonths();
17120                                                 
17121         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17122         
17123         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17124          
17125         prevMonth.setUTCDate(day);
17126         
17127         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17128         
17129         var nextMonth = new Date(prevMonth);
17130         
17131         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17132         
17133         nextMonth = nextMonth.valueOf();
17134         
17135         var fillMonths = false;
17136         
17137         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17138         
17139         while(prevMonth.valueOf() < nextMonth) {
17140             var clsName = '';
17141             
17142             if (prevMonth.getUTCDay() === this.weekStart) {
17143                 if(fillMonths){
17144                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17145                 }
17146                     
17147                 fillMonths = {
17148                     tag: 'tr',
17149                     cn: []
17150                 };
17151                 
17152                 if(this.calendarWeeks){
17153                     // ISO 8601: First week contains first thursday.
17154                     // ISO also states week starts on Monday, but we can be more abstract here.
17155                     var
17156                     // Start of current week: based on weekstart/current date
17157                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17158                     // Thursday of this week
17159                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17160                     // First Thursday of year, year from thursday
17161                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17162                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17163                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17164                     
17165                     fillMonths.cn.push({
17166                         tag: 'td',
17167                         cls: 'cw',
17168                         html: calWeek
17169                     });
17170                 }
17171             }
17172             
17173             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17174                 clsName += ' old';
17175             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17176                 clsName += ' new';
17177             }
17178             if (this.todayHighlight &&
17179                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17180                 prevMonth.getUTCMonth() == today.getMonth() &&
17181                 prevMonth.getUTCDate() == today.getDate()) {
17182                 clsName += ' today';
17183             }
17184             
17185             if (currentDate && prevMonth.valueOf() === currentDate) {
17186                 clsName += ' active';
17187             }
17188             
17189             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17190                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17191                     clsName += ' disabled';
17192             }
17193             
17194             fillMonths.cn.push({
17195                 tag: 'td',
17196                 cls: 'day ' + clsName,
17197                 html: prevMonth.getDate()
17198             });
17199             
17200             prevMonth.setDate(prevMonth.getDate()+1);
17201         }
17202           
17203         var currentYear = this.date && this.date.getUTCFullYear();
17204         var currentMonth = this.date && this.date.getUTCMonth();
17205         
17206         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17207         
17208         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17209             v.removeClass('active');
17210             
17211             if(currentYear === year && k === currentMonth){
17212                 v.addClass('active');
17213             }
17214             
17215             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17216                 v.addClass('disabled');
17217             }
17218             
17219         });
17220         
17221         
17222         year = parseInt(year/10, 10) * 10;
17223         
17224         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17225         
17226         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17227         
17228         year -= 1;
17229         for (var i = -1; i < 11; i++) {
17230             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17231                 tag: 'span',
17232                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17233                 html: year
17234             });
17235             
17236             year += 1;
17237         }
17238     },
17239     
17240     showMode: function(dir) 
17241     {
17242         if (dir) {
17243             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17244         }
17245         
17246         Roo.each(this.picker().select('>div',true).elements, function(v){
17247             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17248             v.hide();
17249         });
17250         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17251     },
17252     
17253     place: function()
17254     {
17255         if(this.isInline) {
17256             return;
17257         }
17258         
17259         this.picker().removeClass(['bottom', 'top']);
17260         
17261         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17262             /*
17263              * place to the top of element!
17264              *
17265              */
17266             
17267             this.picker().addClass('top');
17268             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17269             
17270             return;
17271         }
17272         
17273         this.picker().addClass('bottom');
17274         
17275         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17276     },
17277     
17278     parseDate : function(value)
17279     {
17280         if(!value || value instanceof Date){
17281             return value;
17282         }
17283         var v = Date.parseDate(value, this.format);
17284         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17285             v = Date.parseDate(value, 'Y-m-d');
17286         }
17287         if(!v && this.altFormats){
17288             if(!this.altFormatsArray){
17289                 this.altFormatsArray = this.altFormats.split("|");
17290             }
17291             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17292                 v = Date.parseDate(value, this.altFormatsArray[i]);
17293             }
17294         }
17295         return v;
17296     },
17297     
17298     formatDate : function(date, fmt)
17299     {   
17300         return (!date || !(date instanceof Date)) ?
17301         date : date.dateFormat(fmt || this.format);
17302     },
17303     
17304     onFocus : function()
17305     {
17306         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17307         this.show();
17308     },
17309     
17310     onBlur : function()
17311     {
17312         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17313         
17314         var d = this.inputEl().getValue();
17315         
17316         this.setValue(d);
17317                 
17318         this.hide();
17319     },
17320     
17321     show : function()
17322     {
17323         this.picker().show();
17324         this.update();
17325         this.place();
17326         
17327         this.fireEvent('show', this, this.date);
17328     },
17329     
17330     hide : function()
17331     {
17332         if(this.isInline) {
17333             return;
17334         }
17335         this.picker().hide();
17336         this.viewMode = this.startViewMode;
17337         this.showMode();
17338         
17339         this.fireEvent('hide', this, this.date);
17340         
17341     },
17342     
17343     onMousedown: function(e)
17344     {
17345         e.stopPropagation();
17346         e.preventDefault();
17347     },
17348     
17349     keyup: function(e)
17350     {
17351         Roo.bootstrap.DateField.superclass.keyup.call(this);
17352         this.update();
17353     },
17354
17355     setValue: function(v)
17356     {
17357         if(this.fireEvent('beforeselect', this, v) !== false){
17358             var d = new Date(this.parseDate(v) ).clearTime();
17359         
17360             if(isNaN(d.getTime())){
17361                 this.date = this.viewDate = '';
17362                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17363                 return;
17364             }
17365
17366             v = this.formatDate(d);
17367
17368             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17369
17370             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17371
17372             this.update();
17373
17374             this.fireEvent('select', this, this.date);
17375         }
17376     },
17377     
17378     getValue: function()
17379     {
17380         return this.formatDate(this.date);
17381     },
17382     
17383     fireKey: function(e)
17384     {
17385         if (!this.picker().isVisible()){
17386             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17387                 this.show();
17388             }
17389             return;
17390         }
17391         
17392         var dateChanged = false,
17393         dir, day, month,
17394         newDate, newViewDate;
17395         
17396         switch(e.keyCode){
17397             case 27: // escape
17398                 this.hide();
17399                 e.preventDefault();
17400                 break;
17401             case 37: // left
17402             case 39: // right
17403                 if (!this.keyboardNavigation) {
17404                     break;
17405                 }
17406                 dir = e.keyCode == 37 ? -1 : 1;
17407                 
17408                 if (e.ctrlKey){
17409                     newDate = this.moveYear(this.date, dir);
17410                     newViewDate = this.moveYear(this.viewDate, dir);
17411                 } else if (e.shiftKey){
17412                     newDate = this.moveMonth(this.date, dir);
17413                     newViewDate = this.moveMonth(this.viewDate, dir);
17414                 } else {
17415                     newDate = new Date(this.date);
17416                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17417                     newViewDate = new Date(this.viewDate);
17418                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17419                 }
17420                 if (this.dateWithinRange(newDate)){
17421                     this.date = newDate;
17422                     this.viewDate = newViewDate;
17423                     this.setValue(this.formatDate(this.date));
17424 //                    this.update();
17425                     e.preventDefault();
17426                     dateChanged = true;
17427                 }
17428                 break;
17429             case 38: // up
17430             case 40: // down
17431                 if (!this.keyboardNavigation) {
17432                     break;
17433                 }
17434                 dir = e.keyCode == 38 ? -1 : 1;
17435                 if (e.ctrlKey){
17436                     newDate = this.moveYear(this.date, dir);
17437                     newViewDate = this.moveYear(this.viewDate, dir);
17438                 } else if (e.shiftKey){
17439                     newDate = this.moveMonth(this.date, dir);
17440                     newViewDate = this.moveMonth(this.viewDate, dir);
17441                 } else {
17442                     newDate = new Date(this.date);
17443                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17444                     newViewDate = new Date(this.viewDate);
17445                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17446                 }
17447                 if (this.dateWithinRange(newDate)){
17448                     this.date = newDate;
17449                     this.viewDate = newViewDate;
17450                     this.setValue(this.formatDate(this.date));
17451 //                    this.update();
17452                     e.preventDefault();
17453                     dateChanged = true;
17454                 }
17455                 break;
17456             case 13: // enter
17457                 this.setValue(this.formatDate(this.date));
17458                 this.hide();
17459                 e.preventDefault();
17460                 break;
17461             case 9: // tab
17462                 this.setValue(this.formatDate(this.date));
17463                 this.hide();
17464                 break;
17465             case 16: // shift
17466             case 17: // ctrl
17467             case 18: // alt
17468                 break;
17469             default :
17470                 this.hide();
17471                 
17472         }
17473     },
17474     
17475     
17476     onClick: function(e) 
17477     {
17478         e.stopPropagation();
17479         e.preventDefault();
17480         
17481         var target = e.getTarget();
17482         
17483         if(target.nodeName.toLowerCase() === 'i'){
17484             target = Roo.get(target).dom.parentNode;
17485         }
17486         
17487         var nodeName = target.nodeName;
17488         var className = target.className;
17489         var html = target.innerHTML;
17490         //Roo.log(nodeName);
17491         
17492         switch(nodeName.toLowerCase()) {
17493             case 'th':
17494                 switch(className) {
17495                     case 'switch':
17496                         this.showMode(1);
17497                         break;
17498                     case 'prev':
17499                     case 'next':
17500                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17501                         switch(this.viewMode){
17502                                 case 0:
17503                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17504                                         break;
17505                                 case 1:
17506                                 case 2:
17507                                         this.viewDate = this.moveYear(this.viewDate, dir);
17508                                         break;
17509                         }
17510                         this.fill();
17511                         break;
17512                     case 'today':
17513                         var date = new Date();
17514                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17515 //                        this.fill()
17516                         this.setValue(this.formatDate(this.date));
17517                         
17518                         this.hide();
17519                         break;
17520                 }
17521                 break;
17522             case 'span':
17523                 if (className.indexOf('disabled') < 0) {
17524                     this.viewDate.setUTCDate(1);
17525                     if (className.indexOf('month') > -1) {
17526                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17527                     } else {
17528                         var year = parseInt(html, 10) || 0;
17529                         this.viewDate.setUTCFullYear(year);
17530                         
17531                     }
17532                     
17533                     if(this.singleMode){
17534                         this.setValue(this.formatDate(this.viewDate));
17535                         this.hide();
17536                         return;
17537                     }
17538                     
17539                     this.showMode(-1);
17540                     this.fill();
17541                 }
17542                 break;
17543                 
17544             case 'td':
17545                 //Roo.log(className);
17546                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17547                     var day = parseInt(html, 10) || 1;
17548                     var year = this.viewDate.getUTCFullYear(),
17549                         month = this.viewDate.getUTCMonth();
17550
17551                     if (className.indexOf('old') > -1) {
17552                         if(month === 0 ){
17553                             month = 11;
17554                             year -= 1;
17555                         }else{
17556                             month -= 1;
17557                         }
17558                     } else if (className.indexOf('new') > -1) {
17559                         if (month == 11) {
17560                             month = 0;
17561                             year += 1;
17562                         } else {
17563                             month += 1;
17564                         }
17565                     }
17566                     //Roo.log([year,month,day]);
17567                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17568                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17569 //                    this.fill();
17570                     //Roo.log(this.formatDate(this.date));
17571                     this.setValue(this.formatDate(this.date));
17572                     this.hide();
17573                 }
17574                 break;
17575         }
17576     },
17577     
17578     setStartDate: function(startDate)
17579     {
17580         this.startDate = startDate || -Infinity;
17581         if (this.startDate !== -Infinity) {
17582             this.startDate = this.parseDate(this.startDate);
17583         }
17584         this.update();
17585         this.updateNavArrows();
17586     },
17587
17588     setEndDate: function(endDate)
17589     {
17590         this.endDate = endDate || Infinity;
17591         if (this.endDate !== Infinity) {
17592             this.endDate = this.parseDate(this.endDate);
17593         }
17594         this.update();
17595         this.updateNavArrows();
17596     },
17597     
17598     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17599     {
17600         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17601         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17602             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17603         }
17604         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17605             return parseInt(d, 10);
17606         });
17607         this.update();
17608         this.updateNavArrows();
17609     },
17610     
17611     updateNavArrows: function() 
17612     {
17613         if(this.singleMode){
17614             return;
17615         }
17616         
17617         var d = new Date(this.viewDate),
17618         year = d.getUTCFullYear(),
17619         month = d.getUTCMonth();
17620         
17621         Roo.each(this.picker().select('.prev', true).elements, function(v){
17622             v.show();
17623             switch (this.viewMode) {
17624                 case 0:
17625
17626                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17627                         v.hide();
17628                     }
17629                     break;
17630                 case 1:
17631                 case 2:
17632                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17633                         v.hide();
17634                     }
17635                     break;
17636             }
17637         });
17638         
17639         Roo.each(this.picker().select('.next', true).elements, function(v){
17640             v.show();
17641             switch (this.viewMode) {
17642                 case 0:
17643
17644                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17645                         v.hide();
17646                     }
17647                     break;
17648                 case 1:
17649                 case 2:
17650                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17651                         v.hide();
17652                     }
17653                     break;
17654             }
17655         })
17656     },
17657     
17658     moveMonth: function(date, dir)
17659     {
17660         if (!dir) {
17661             return date;
17662         }
17663         var new_date = new Date(date.valueOf()),
17664         day = new_date.getUTCDate(),
17665         month = new_date.getUTCMonth(),
17666         mag = Math.abs(dir),
17667         new_month, test;
17668         dir = dir > 0 ? 1 : -1;
17669         if (mag == 1){
17670             test = dir == -1
17671             // If going back one month, make sure month is not current month
17672             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17673             ? function(){
17674                 return new_date.getUTCMonth() == month;
17675             }
17676             // If going forward one month, make sure month is as expected
17677             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17678             : function(){
17679                 return new_date.getUTCMonth() != new_month;
17680             };
17681             new_month = month + dir;
17682             new_date.setUTCMonth(new_month);
17683             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17684             if (new_month < 0 || new_month > 11) {
17685                 new_month = (new_month + 12) % 12;
17686             }
17687         } else {
17688             // For magnitudes >1, move one month at a time...
17689             for (var i=0; i<mag; i++) {
17690                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17691                 new_date = this.moveMonth(new_date, dir);
17692             }
17693             // ...then reset the day, keeping it in the new month
17694             new_month = new_date.getUTCMonth();
17695             new_date.setUTCDate(day);
17696             test = function(){
17697                 return new_month != new_date.getUTCMonth();
17698             };
17699         }
17700         // Common date-resetting loop -- if date is beyond end of month, make it
17701         // end of month
17702         while (test()){
17703             new_date.setUTCDate(--day);
17704             new_date.setUTCMonth(new_month);
17705         }
17706         return new_date;
17707     },
17708
17709     moveYear: function(date, dir)
17710     {
17711         return this.moveMonth(date, dir*12);
17712     },
17713
17714     dateWithinRange: function(date)
17715     {
17716         return date >= this.startDate && date <= this.endDate;
17717     },
17718
17719     
17720     remove: function() 
17721     {
17722         this.picker().remove();
17723     }
17724    
17725 });
17726
17727 Roo.apply(Roo.bootstrap.DateField,  {
17728     
17729     head : {
17730         tag: 'thead',
17731         cn: [
17732         {
17733             tag: 'tr',
17734             cn: [
17735             {
17736                 tag: 'th',
17737                 cls: 'prev',
17738                 html: '<i class="fa fa-arrow-left"/>'
17739             },
17740             {
17741                 tag: 'th',
17742                 cls: 'switch',
17743                 colspan: '5'
17744             },
17745             {
17746                 tag: 'th',
17747                 cls: 'next',
17748                 html: '<i class="fa fa-arrow-right"/>'
17749             }
17750
17751             ]
17752         }
17753         ]
17754     },
17755     
17756     content : {
17757         tag: 'tbody',
17758         cn: [
17759         {
17760             tag: 'tr',
17761             cn: [
17762             {
17763                 tag: 'td',
17764                 colspan: '7'
17765             }
17766             ]
17767         }
17768         ]
17769     },
17770     
17771     footer : {
17772         tag: 'tfoot',
17773         cn: [
17774         {
17775             tag: 'tr',
17776             cn: [
17777             {
17778                 tag: 'th',
17779                 colspan: '7',
17780                 cls: 'today'
17781             }
17782                     
17783             ]
17784         }
17785         ]
17786     },
17787     
17788     dates:{
17789         en: {
17790             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17791             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17792             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17793             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17794             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17795             today: "Today"
17796         }
17797     },
17798     
17799     modes: [
17800     {
17801         clsName: 'days',
17802         navFnc: 'Month',
17803         navStep: 1
17804     },
17805     {
17806         clsName: 'months',
17807         navFnc: 'FullYear',
17808         navStep: 1
17809     },
17810     {
17811         clsName: 'years',
17812         navFnc: 'FullYear',
17813         navStep: 10
17814     }]
17815 });
17816
17817 Roo.apply(Roo.bootstrap.DateField,  {
17818   
17819     template : {
17820         tag: 'div',
17821         cls: 'datepicker dropdown-menu roo-dynamic',
17822         cn: [
17823         {
17824             tag: 'div',
17825             cls: 'datepicker-days',
17826             cn: [
17827             {
17828                 tag: 'table',
17829                 cls: 'table-condensed',
17830                 cn:[
17831                 Roo.bootstrap.DateField.head,
17832                 {
17833                     tag: 'tbody'
17834                 },
17835                 Roo.bootstrap.DateField.footer
17836                 ]
17837             }
17838             ]
17839         },
17840         {
17841             tag: 'div',
17842             cls: 'datepicker-months',
17843             cn: [
17844             {
17845                 tag: 'table',
17846                 cls: 'table-condensed',
17847                 cn:[
17848                 Roo.bootstrap.DateField.head,
17849                 Roo.bootstrap.DateField.content,
17850                 Roo.bootstrap.DateField.footer
17851                 ]
17852             }
17853             ]
17854         },
17855         {
17856             tag: 'div',
17857             cls: 'datepicker-years',
17858             cn: [
17859             {
17860                 tag: 'table',
17861                 cls: 'table-condensed',
17862                 cn:[
17863                 Roo.bootstrap.DateField.head,
17864                 Roo.bootstrap.DateField.content,
17865                 Roo.bootstrap.DateField.footer
17866                 ]
17867             }
17868             ]
17869         }
17870         ]
17871     }
17872 });
17873
17874  
17875
17876  /*
17877  * - LGPL
17878  *
17879  * TimeField
17880  * 
17881  */
17882
17883 /**
17884  * @class Roo.bootstrap.TimeField
17885  * @extends Roo.bootstrap.Input
17886  * Bootstrap DateField class
17887  * 
17888  * 
17889  * @constructor
17890  * Create a new TimeField
17891  * @param {Object} config The config object
17892  */
17893
17894 Roo.bootstrap.TimeField = function(config){
17895     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17896     this.addEvents({
17897             /**
17898              * @event show
17899              * Fires when this field show.
17900              * @param {Roo.bootstrap.DateField} thisthis
17901              * @param {Mixed} date The date value
17902              */
17903             show : true,
17904             /**
17905              * @event show
17906              * Fires when this field hide.
17907              * @param {Roo.bootstrap.DateField} this
17908              * @param {Mixed} date The date value
17909              */
17910             hide : true,
17911             /**
17912              * @event select
17913              * Fires when select a date.
17914              * @param {Roo.bootstrap.DateField} this
17915              * @param {Mixed} date The date value
17916              */
17917             select : true
17918         });
17919 };
17920
17921 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17922     
17923     /**
17924      * @cfg {String} format
17925      * The default time format string which can be overriden for localization support.  The format must be
17926      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17927      */
17928     format : "H:i",
17929        
17930     onRender: function(ct, position)
17931     {
17932         
17933         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17934                 
17935         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17936         
17937         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17938         
17939         this.pop = this.picker().select('>.datepicker-time',true).first();
17940         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17941         
17942         this.picker().on('mousedown', this.onMousedown, this);
17943         this.picker().on('click', this.onClick, this);
17944         
17945         this.picker().addClass('datepicker-dropdown');
17946     
17947         this.fillTime();
17948         this.update();
17949             
17950         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17951         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17952         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17953         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17954         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17955         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17956
17957     },
17958     
17959     fireKey: function(e){
17960         if (!this.picker().isVisible()){
17961             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17962                 this.show();
17963             }
17964             return;
17965         }
17966
17967         e.preventDefault();
17968         
17969         switch(e.keyCode){
17970             case 27: // escape
17971                 this.hide();
17972                 break;
17973             case 37: // left
17974             case 39: // right
17975                 this.onTogglePeriod();
17976                 break;
17977             case 38: // up
17978                 this.onIncrementMinutes();
17979                 break;
17980             case 40: // down
17981                 this.onDecrementMinutes();
17982                 break;
17983             case 13: // enter
17984             case 9: // tab
17985                 this.setTime();
17986                 break;
17987         }
17988     },
17989     
17990     onClick: function(e) {
17991         e.stopPropagation();
17992         e.preventDefault();
17993     },
17994     
17995     picker : function()
17996     {
17997         return this.el.select('.datepicker', true).first();
17998     },
17999     
18000     fillTime: function()
18001     {    
18002         var time = this.pop.select('tbody', true).first();
18003         
18004         time.dom.innerHTML = '';
18005         
18006         time.createChild({
18007             tag: 'tr',
18008             cn: [
18009                 {
18010                     tag: 'td',
18011                     cn: [
18012                         {
18013                             tag: 'a',
18014                             href: '#',
18015                             cls: 'btn',
18016                             cn: [
18017                                 {
18018                                     tag: 'span',
18019                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18020                                 }
18021                             ]
18022                         } 
18023                     ]
18024                 },
18025                 {
18026                     tag: 'td',
18027                     cls: 'separator'
18028                 },
18029                 {
18030                     tag: 'td',
18031                     cn: [
18032                         {
18033                             tag: 'a',
18034                             href: '#',
18035                             cls: 'btn',
18036                             cn: [
18037                                 {
18038                                     tag: 'span',
18039                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18040                                 }
18041                             ]
18042                         }
18043                     ]
18044                 },
18045                 {
18046                     tag: 'td',
18047                     cls: 'separator'
18048                 }
18049             ]
18050         });
18051         
18052         time.createChild({
18053             tag: 'tr',
18054             cn: [
18055                 {
18056                     tag: 'td',
18057                     cn: [
18058                         {
18059                             tag: 'span',
18060                             cls: 'timepicker-hour',
18061                             html: '00'
18062                         }  
18063                     ]
18064                 },
18065                 {
18066                     tag: 'td',
18067                     cls: 'separator',
18068                     html: ':'
18069                 },
18070                 {
18071                     tag: 'td',
18072                     cn: [
18073                         {
18074                             tag: 'span',
18075                             cls: 'timepicker-minute',
18076                             html: '00'
18077                         }  
18078                     ]
18079                 },
18080                 {
18081                     tag: 'td',
18082                     cls: 'separator'
18083                 },
18084                 {
18085                     tag: 'td',
18086                     cn: [
18087                         {
18088                             tag: 'button',
18089                             type: 'button',
18090                             cls: 'btn btn-primary period',
18091                             html: 'AM'
18092                             
18093                         }
18094                     ]
18095                 }
18096             ]
18097         });
18098         
18099         time.createChild({
18100             tag: 'tr',
18101             cn: [
18102                 {
18103                     tag: 'td',
18104                     cn: [
18105                         {
18106                             tag: 'a',
18107                             href: '#',
18108                             cls: 'btn',
18109                             cn: [
18110                                 {
18111                                     tag: 'span',
18112                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18113                                 }
18114                             ]
18115                         }
18116                     ]
18117                 },
18118                 {
18119                     tag: 'td',
18120                     cls: 'separator'
18121                 },
18122                 {
18123                     tag: 'td',
18124                     cn: [
18125                         {
18126                             tag: 'a',
18127                             href: '#',
18128                             cls: 'btn',
18129                             cn: [
18130                                 {
18131                                     tag: 'span',
18132                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18133                                 }
18134                             ]
18135                         }
18136                     ]
18137                 },
18138                 {
18139                     tag: 'td',
18140                     cls: 'separator'
18141                 }
18142             ]
18143         });
18144         
18145     },
18146     
18147     update: function()
18148     {
18149         
18150         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18151         
18152         this.fill();
18153     },
18154     
18155     fill: function() 
18156     {
18157         var hours = this.time.getHours();
18158         var minutes = this.time.getMinutes();
18159         var period = 'AM';
18160         
18161         if(hours > 11){
18162             period = 'PM';
18163         }
18164         
18165         if(hours == 0){
18166             hours = 12;
18167         }
18168         
18169         
18170         if(hours > 12){
18171             hours = hours - 12;
18172         }
18173         
18174         if(hours < 10){
18175             hours = '0' + hours;
18176         }
18177         
18178         if(minutes < 10){
18179             minutes = '0' + minutes;
18180         }
18181         
18182         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18183         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18184         this.pop.select('button', true).first().dom.innerHTML = period;
18185         
18186     },
18187     
18188     place: function()
18189     {   
18190         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18191         
18192         var cls = ['bottom'];
18193         
18194         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18195             cls.pop();
18196             cls.push('top');
18197         }
18198         
18199         cls.push('right');
18200         
18201         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18202             cls.pop();
18203             cls.push('left');
18204         }
18205         
18206         this.picker().addClass(cls.join('-'));
18207         
18208         var _this = this;
18209         
18210         Roo.each(cls, function(c){
18211             if(c == 'bottom'){
18212                 _this.picker().setTop(_this.inputEl().getHeight());
18213                 return;
18214             }
18215             if(c == 'top'){
18216                 _this.picker().setTop(0 - _this.picker().getHeight());
18217                 return;
18218             }
18219             
18220             if(c == 'left'){
18221                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18222                 return;
18223             }
18224             if(c == 'right'){
18225                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18226                 return;
18227             }
18228         });
18229         
18230     },
18231   
18232     onFocus : function()
18233     {
18234         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18235         this.show();
18236     },
18237     
18238     onBlur : function()
18239     {
18240         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18241         this.hide();
18242     },
18243     
18244     show : function()
18245     {
18246         this.picker().show();
18247         this.pop.show();
18248         this.update();
18249         this.place();
18250         
18251         this.fireEvent('show', this, this.date);
18252     },
18253     
18254     hide : function()
18255     {
18256         this.picker().hide();
18257         this.pop.hide();
18258         
18259         this.fireEvent('hide', this, this.date);
18260     },
18261     
18262     setTime : function()
18263     {
18264         this.hide();
18265         this.setValue(this.time.format(this.format));
18266         
18267         this.fireEvent('select', this, this.date);
18268         
18269         
18270     },
18271     
18272     onMousedown: function(e){
18273         e.stopPropagation();
18274         e.preventDefault();
18275     },
18276     
18277     onIncrementHours: function()
18278     {
18279         Roo.log('onIncrementHours');
18280         this.time = this.time.add(Date.HOUR, 1);
18281         this.update();
18282         
18283     },
18284     
18285     onDecrementHours: function()
18286     {
18287         Roo.log('onDecrementHours');
18288         this.time = this.time.add(Date.HOUR, -1);
18289         this.update();
18290     },
18291     
18292     onIncrementMinutes: function()
18293     {
18294         Roo.log('onIncrementMinutes');
18295         this.time = this.time.add(Date.MINUTE, 1);
18296         this.update();
18297     },
18298     
18299     onDecrementMinutes: function()
18300     {
18301         Roo.log('onDecrementMinutes');
18302         this.time = this.time.add(Date.MINUTE, -1);
18303         this.update();
18304     },
18305     
18306     onTogglePeriod: function()
18307     {
18308         Roo.log('onTogglePeriod');
18309         this.time = this.time.add(Date.HOUR, 12);
18310         this.update();
18311     }
18312     
18313    
18314 });
18315
18316 Roo.apply(Roo.bootstrap.TimeField,  {
18317     
18318     content : {
18319         tag: 'tbody',
18320         cn: [
18321             {
18322                 tag: 'tr',
18323                 cn: [
18324                 {
18325                     tag: 'td',
18326                     colspan: '7'
18327                 }
18328                 ]
18329             }
18330         ]
18331     },
18332     
18333     footer : {
18334         tag: 'tfoot',
18335         cn: [
18336             {
18337                 tag: 'tr',
18338                 cn: [
18339                 {
18340                     tag: 'th',
18341                     colspan: '7',
18342                     cls: '',
18343                     cn: [
18344                         {
18345                             tag: 'button',
18346                             cls: 'btn btn-info ok',
18347                             html: 'OK'
18348                         }
18349                     ]
18350                 }
18351
18352                 ]
18353             }
18354         ]
18355     }
18356 });
18357
18358 Roo.apply(Roo.bootstrap.TimeField,  {
18359   
18360     template : {
18361         tag: 'div',
18362         cls: 'datepicker dropdown-menu',
18363         cn: [
18364             {
18365                 tag: 'div',
18366                 cls: 'datepicker-time',
18367                 cn: [
18368                 {
18369                     tag: 'table',
18370                     cls: 'table-condensed',
18371                     cn:[
18372                     Roo.bootstrap.TimeField.content,
18373                     Roo.bootstrap.TimeField.footer
18374                     ]
18375                 }
18376                 ]
18377             }
18378         ]
18379     }
18380 });
18381
18382  
18383
18384  /*
18385  * - LGPL
18386  *
18387  * MonthField
18388  * 
18389  */
18390
18391 /**
18392  * @class Roo.bootstrap.MonthField
18393  * @extends Roo.bootstrap.Input
18394  * Bootstrap MonthField class
18395  * 
18396  * @cfg {String} language default en
18397  * 
18398  * @constructor
18399  * Create a new MonthField
18400  * @param {Object} config The config object
18401  */
18402
18403 Roo.bootstrap.MonthField = function(config){
18404     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18405     
18406     this.addEvents({
18407         /**
18408          * @event show
18409          * Fires when this field show.
18410          * @param {Roo.bootstrap.MonthField} this
18411          * @param {Mixed} date The date value
18412          */
18413         show : true,
18414         /**
18415          * @event show
18416          * Fires when this field hide.
18417          * @param {Roo.bootstrap.MonthField} this
18418          * @param {Mixed} date The date value
18419          */
18420         hide : true,
18421         /**
18422          * @event select
18423          * Fires when select a date.
18424          * @param {Roo.bootstrap.MonthField} this
18425          * @param {String} oldvalue The old value
18426          * @param {String} newvalue The new value
18427          */
18428         select : true
18429     });
18430 };
18431
18432 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18433     
18434     onRender: function(ct, position)
18435     {
18436         
18437         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18438         
18439         this.language = this.language || 'en';
18440         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18441         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18442         
18443         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18444         this.isInline = false;
18445         this.isInput = true;
18446         this.component = this.el.select('.add-on', true).first() || false;
18447         this.component = (this.component && this.component.length === 0) ? false : this.component;
18448         this.hasInput = this.component && this.inputEL().length;
18449         
18450         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18451         
18452         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18453         
18454         this.picker().on('mousedown', this.onMousedown, this);
18455         this.picker().on('click', this.onClick, this);
18456         
18457         this.picker().addClass('datepicker-dropdown');
18458         
18459         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18460             v.setStyle('width', '189px');
18461         });
18462         
18463         this.fillMonths();
18464         
18465         this.update();
18466         
18467         if(this.isInline) {
18468             this.show();
18469         }
18470         
18471     },
18472     
18473     setValue: function(v, suppressEvent)
18474     {   
18475         var o = this.getValue();
18476         
18477         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18478         
18479         this.update();
18480
18481         if(suppressEvent !== true){
18482             this.fireEvent('select', this, o, v);
18483         }
18484         
18485     },
18486     
18487     getValue: function()
18488     {
18489         return this.value;
18490     },
18491     
18492     onClick: function(e) 
18493     {
18494         e.stopPropagation();
18495         e.preventDefault();
18496         
18497         var target = e.getTarget();
18498         
18499         if(target.nodeName.toLowerCase() === 'i'){
18500             target = Roo.get(target).dom.parentNode;
18501         }
18502         
18503         var nodeName = target.nodeName;
18504         var className = target.className;
18505         var html = target.innerHTML;
18506         
18507         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18508             return;
18509         }
18510         
18511         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18512         
18513         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18514         
18515         this.hide();
18516                         
18517     },
18518     
18519     picker : function()
18520     {
18521         return this.pickerEl;
18522     },
18523     
18524     fillMonths: function()
18525     {    
18526         var i = 0;
18527         var months = this.picker().select('>.datepicker-months td', true).first();
18528         
18529         months.dom.innerHTML = '';
18530         
18531         while (i < 12) {
18532             var month = {
18533                 tag: 'span',
18534                 cls: 'month',
18535                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18536             };
18537             
18538             months.createChild(month);
18539         }
18540         
18541     },
18542     
18543     update: function()
18544     {
18545         var _this = this;
18546         
18547         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18548             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18549         }
18550         
18551         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18552             e.removeClass('active');
18553             
18554             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18555                 e.addClass('active');
18556             }
18557         })
18558     },
18559     
18560     place: function()
18561     {
18562         if(this.isInline) {
18563             return;
18564         }
18565         
18566         this.picker().removeClass(['bottom', 'top']);
18567         
18568         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18569             /*
18570              * place to the top of element!
18571              *
18572              */
18573             
18574             this.picker().addClass('top');
18575             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18576             
18577             return;
18578         }
18579         
18580         this.picker().addClass('bottom');
18581         
18582         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18583     },
18584     
18585     onFocus : function()
18586     {
18587         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18588         this.show();
18589     },
18590     
18591     onBlur : function()
18592     {
18593         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18594         
18595         var d = this.inputEl().getValue();
18596         
18597         this.setValue(d);
18598                 
18599         this.hide();
18600     },
18601     
18602     show : function()
18603     {
18604         this.picker().show();
18605         this.picker().select('>.datepicker-months', true).first().show();
18606         this.update();
18607         this.place();
18608         
18609         this.fireEvent('show', this, this.date);
18610     },
18611     
18612     hide : function()
18613     {
18614         if(this.isInline) {
18615             return;
18616         }
18617         this.picker().hide();
18618         this.fireEvent('hide', this, this.date);
18619         
18620     },
18621     
18622     onMousedown: function(e)
18623     {
18624         e.stopPropagation();
18625         e.preventDefault();
18626     },
18627     
18628     keyup: function(e)
18629     {
18630         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18631         this.update();
18632     },
18633
18634     fireKey: function(e)
18635     {
18636         if (!this.picker().isVisible()){
18637             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18638                 this.show();
18639             }
18640             return;
18641         }
18642         
18643         var dir;
18644         
18645         switch(e.keyCode){
18646             case 27: // escape
18647                 this.hide();
18648                 e.preventDefault();
18649                 break;
18650             case 37: // left
18651             case 39: // right
18652                 dir = e.keyCode == 37 ? -1 : 1;
18653                 
18654                 this.vIndex = this.vIndex + dir;
18655                 
18656                 if(this.vIndex < 0){
18657                     this.vIndex = 0;
18658                 }
18659                 
18660                 if(this.vIndex > 11){
18661                     this.vIndex = 11;
18662                 }
18663                 
18664                 if(isNaN(this.vIndex)){
18665                     this.vIndex = 0;
18666                 }
18667                 
18668                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18669                 
18670                 break;
18671             case 38: // up
18672             case 40: // down
18673                 
18674                 dir = e.keyCode == 38 ? -1 : 1;
18675                 
18676                 this.vIndex = this.vIndex + dir * 4;
18677                 
18678                 if(this.vIndex < 0){
18679                     this.vIndex = 0;
18680                 }
18681                 
18682                 if(this.vIndex > 11){
18683                     this.vIndex = 11;
18684                 }
18685                 
18686                 if(isNaN(this.vIndex)){
18687                     this.vIndex = 0;
18688                 }
18689                 
18690                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18691                 break;
18692                 
18693             case 13: // enter
18694                 
18695                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18696                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18697                 }
18698                 
18699                 this.hide();
18700                 e.preventDefault();
18701                 break;
18702             case 9: // tab
18703                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18704                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18705                 }
18706                 this.hide();
18707                 break;
18708             case 16: // shift
18709             case 17: // ctrl
18710             case 18: // alt
18711                 break;
18712             default :
18713                 this.hide();
18714                 
18715         }
18716     },
18717     
18718     remove: function() 
18719     {
18720         this.picker().remove();
18721     }
18722    
18723 });
18724
18725 Roo.apply(Roo.bootstrap.MonthField,  {
18726     
18727     content : {
18728         tag: 'tbody',
18729         cn: [
18730         {
18731             tag: 'tr',
18732             cn: [
18733             {
18734                 tag: 'td',
18735                 colspan: '7'
18736             }
18737             ]
18738         }
18739         ]
18740     },
18741     
18742     dates:{
18743         en: {
18744             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18745             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18746         }
18747     }
18748 });
18749
18750 Roo.apply(Roo.bootstrap.MonthField,  {
18751   
18752     template : {
18753         tag: 'div',
18754         cls: 'datepicker dropdown-menu roo-dynamic',
18755         cn: [
18756             {
18757                 tag: 'div',
18758                 cls: 'datepicker-months',
18759                 cn: [
18760                 {
18761                     tag: 'table',
18762                     cls: 'table-condensed',
18763                     cn:[
18764                         Roo.bootstrap.DateField.content
18765                     ]
18766                 }
18767                 ]
18768             }
18769         ]
18770     }
18771 });
18772
18773  
18774
18775  
18776  /*
18777  * - LGPL
18778  *
18779  * CheckBox
18780  * 
18781  */
18782
18783 /**
18784  * @class Roo.bootstrap.CheckBox
18785  * @extends Roo.bootstrap.Input
18786  * Bootstrap CheckBox class
18787  * 
18788  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18789  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18790  * @cfg {String} boxLabel The text that appears beside the checkbox
18791  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18792  * @cfg {Boolean} checked initnal the element
18793  * @cfg {Boolean} inline inline the element (default false)
18794  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18795  * 
18796  * @constructor
18797  * Create a new CheckBox
18798  * @param {Object} config The config object
18799  */
18800
18801 Roo.bootstrap.CheckBox = function(config){
18802     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18803    
18804     this.addEvents({
18805         /**
18806         * @event check
18807         * Fires when the element is checked or unchecked.
18808         * @param {Roo.bootstrap.CheckBox} this This input
18809         * @param {Boolean} checked The new checked value
18810         */
18811        check : true
18812     });
18813     
18814 };
18815
18816 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18817   
18818     inputType: 'checkbox',
18819     inputValue: 1,
18820     valueOff: 0,
18821     boxLabel: false,
18822     checked: false,
18823     weight : false,
18824     inline: false,
18825     
18826     getAutoCreate : function()
18827     {
18828         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18829         
18830         var id = Roo.id();
18831         
18832         var cfg = {};
18833         
18834         cfg.cls = 'form-group ' + this.inputType; //input-group
18835         
18836         if(this.inline){
18837             cfg.cls += ' ' + this.inputType + '-inline';
18838         }
18839         
18840         var input =  {
18841             tag: 'input',
18842             id : id,
18843             type : this.inputType,
18844             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18845             cls : 'roo-' + this.inputType, //'form-box',
18846             placeholder : this.placeholder || ''
18847             
18848         };
18849         
18850         if (this.weight) { // Validity check?
18851             cfg.cls += " " + this.inputType + "-" + this.weight;
18852         }
18853         
18854         if (this.disabled) {
18855             input.disabled=true;
18856         }
18857         
18858         if(this.checked){
18859             input.checked = this.checked;
18860         }
18861         
18862         if (this.name) {
18863             input.name = this.name;
18864         }
18865         
18866         if (this.size) {
18867             input.cls += ' input-' + this.size;
18868         }
18869         
18870         var settings=this;
18871         
18872         ['xs','sm','md','lg'].map(function(size){
18873             if (settings[size]) {
18874                 cfg.cls += ' col-' + size + '-' + settings[size];
18875             }
18876         });
18877         
18878         var inputblock = input;
18879          
18880         if (this.before || this.after) {
18881             
18882             inputblock = {
18883                 cls : 'input-group',
18884                 cn :  [] 
18885             };
18886             
18887             if (this.before) {
18888                 inputblock.cn.push({
18889                     tag :'span',
18890                     cls : 'input-group-addon',
18891                     html : this.before
18892                 });
18893             }
18894             
18895             inputblock.cn.push(input);
18896             
18897             if (this.after) {
18898                 inputblock.cn.push({
18899                     tag :'span',
18900                     cls : 'input-group-addon',
18901                     html : this.after
18902                 });
18903             }
18904             
18905         }
18906         
18907         if (align ==='left' && this.fieldLabel.length) {
18908 //                Roo.log("left and has label");
18909                 cfg.cn = [
18910                     
18911                     {
18912                         tag: 'label',
18913                         'for' :  id,
18914                         cls : 'control-label col-md-' + this.labelWidth,
18915                         html : this.fieldLabel
18916                         
18917                     },
18918                     {
18919                         cls : "col-md-" + (12 - this.labelWidth), 
18920                         cn: [
18921                             inputblock
18922                         ]
18923                     }
18924                     
18925                 ];
18926         } else if ( this.fieldLabel.length) {
18927 //                Roo.log(" label");
18928                 cfg.cn = [
18929                    
18930                     {
18931                         tag: this.boxLabel ? 'span' : 'label',
18932                         'for': id,
18933                         cls: 'control-label box-input-label',
18934                         //cls : 'input-group-addon',
18935                         html : this.fieldLabel
18936                         
18937                     },
18938                     
18939                     inputblock
18940                     
18941                 ];
18942
18943         } else {
18944             
18945 //                Roo.log(" no label && no align");
18946                 cfg.cn = [  inputblock ] ;
18947                 
18948                 
18949         }
18950         
18951         if(this.boxLabel){
18952              var boxLabelCfg = {
18953                 tag: 'label',
18954                 //'for': id, // box label is handled by onclick - so no for...
18955                 cls: 'box-label',
18956                 html: this.boxLabel
18957             };
18958             
18959             if(this.tooltip){
18960                 boxLabelCfg.tooltip = this.tooltip;
18961             }
18962              
18963             cfg.cn.push(boxLabelCfg);
18964         }
18965         
18966         
18967        
18968         return cfg;
18969         
18970     },
18971     
18972     /**
18973      * return the real input element.
18974      */
18975     inputEl: function ()
18976     {
18977         return this.el.select('input.roo-' + this.inputType,true).first();
18978     },
18979     
18980     labelEl: function()
18981     {
18982         return this.el.select('label.control-label',true).first();
18983     },
18984     /* depricated... */
18985     
18986     label: function()
18987     {
18988         return this.labelEl();
18989     },
18990     
18991     boxLabelEl: function()
18992     {
18993         return this.el.select('label.box-label',true).first();
18994     },
18995     
18996     initEvents : function()
18997     {
18998 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18999         
19000         this.inputEl().on('click', this.onClick,  this);
19001         
19002         if (this.boxLabel) { 
19003             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19004         }
19005         
19006         this.startValue = this.getValue();
19007         
19008         if(this.groupId){
19009             Roo.bootstrap.CheckBox.register(this);
19010         }
19011     },
19012     
19013     onClick : function()
19014     {   
19015         this.setChecked(!this.checked);
19016     },
19017     
19018     setChecked : function(state,suppressEvent)
19019     {
19020         this.startValue = this.getValue();
19021         
19022         if(this.inputType == 'radio'){
19023             
19024             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19025                 e.dom.checked = false;
19026             });
19027             
19028             this.inputEl().dom.checked = true;
19029             
19030             this.inputEl().dom.value = this.inputValue;
19031             
19032             if(suppressEvent !== true){
19033                 this.fireEvent('check', this, true);
19034             }
19035             
19036             this.validate();
19037             
19038             return;
19039         }
19040         
19041         this.checked = state;
19042         
19043         this.inputEl().dom.checked = state;
19044         
19045         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19046         
19047         if(suppressEvent !== true){
19048             this.fireEvent('check', this, state);
19049         }
19050         
19051         this.validate();
19052     },
19053     
19054     getValue : function()
19055     {
19056         if(this.inputType == 'radio'){
19057             return this.getGroupValue();
19058         }
19059         
19060         return this.inputEl().getValue();
19061         
19062     },
19063     
19064     getGroupValue : function()
19065     {
19066         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19067             return '';
19068         }
19069         
19070         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19071     },
19072     
19073     setValue : function(v,suppressEvent)
19074     {
19075         if(this.inputType == 'radio'){
19076             this.setGroupValue(v, suppressEvent);
19077             return;
19078         }
19079         
19080         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19081         
19082         this.validate();
19083     },
19084     
19085     setGroupValue : function(v, suppressEvent)
19086     {
19087         this.startValue = this.getValue();
19088         
19089         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19090             e.dom.checked = false;
19091             
19092             if(e.dom.value == v){
19093                 e.dom.checked = true;
19094             }
19095         });
19096         
19097         if(suppressEvent !== true){
19098             this.fireEvent('check', this, true);
19099         }
19100
19101         this.validate();
19102         
19103         return;
19104     },
19105     
19106     validate : function()
19107     {
19108         if(
19109                 this.disabled || 
19110                 (this.inputType == 'radio' && this.validateRadio()) ||
19111                 (this.inputType == 'checkbox' && this.validateCheckbox())
19112         ){
19113             this.markValid();
19114             return true;
19115         }
19116         
19117         this.markInvalid();
19118         return false;
19119     },
19120     
19121     validateRadio : function()
19122     {
19123         var valid = false;
19124         
19125         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19126             if(!e.dom.checked){
19127                 return;
19128             }
19129             
19130             valid = true;
19131             
19132             return false;
19133         });
19134         
19135         return valid;
19136     },
19137     
19138     validateCheckbox : function()
19139     {
19140         if(!this.groupId){
19141             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19142         }
19143         
19144         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19145         
19146         if(!group){
19147             return false;
19148         }
19149         
19150         var r = false;
19151         
19152         for(var i in group){
19153             if(r){
19154                 break;
19155             }
19156             
19157             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19158         }
19159         
19160         return r;
19161     },
19162     
19163     /**
19164      * Mark this field as valid
19165      */
19166     markValid : function()
19167     {
19168         if(this.allowBlank){
19169             return;
19170         }
19171         
19172         var _this = this;
19173         
19174         this.fireEvent('valid', this);
19175         
19176         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19177         
19178         if(this.groupId){
19179             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19180         }
19181         
19182         if(label){
19183             label.markValid();
19184         }
19185         
19186         if(this.inputType == 'radio'){
19187             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19188                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19189                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19190             });
19191             
19192             return;
19193         }
19194         
19195         if(!this.groupId){
19196             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19197             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19198             return;
19199         }
19200         
19201         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19202             
19203         if(!group){
19204             return;
19205         }
19206         
19207         for(var i in group){
19208             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19209             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19210         }
19211     },
19212     
19213      /**
19214      * Mark this field as invalid
19215      * @param {String} msg The validation message
19216      */
19217     markInvalid : function(msg)
19218     {
19219         if(this.allowBlank){
19220             return;
19221         }
19222         
19223         var _this = this;
19224         
19225         this.fireEvent('invalid', this, msg);
19226         
19227         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19228         
19229         if(this.groupId){
19230             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19231         }
19232         
19233         if(label){
19234             label.markInvalid();
19235         }
19236             
19237         if(this.inputType == 'radio'){
19238             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19239                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19240                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19241             });
19242             
19243             return;
19244         }
19245         
19246         if(!this.groupId){
19247             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19248             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19249             return;
19250         }
19251         
19252         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19253         
19254         if(!group){
19255             return;
19256         }
19257         
19258         for(var i in group){
19259             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19260             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19261         }
19262         
19263     }
19264     
19265 });
19266
19267 Roo.apply(Roo.bootstrap.CheckBox, {
19268     
19269     groups: {},
19270     
19271      /**
19272     * register a CheckBox Group
19273     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19274     */
19275     register : function(checkbox)
19276     {
19277         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19278             this.groups[checkbox.groupId] = {};
19279         }
19280         
19281         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19282             return;
19283         }
19284         
19285         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19286         
19287     },
19288     /**
19289     * fetch a CheckBox Group based on the group ID
19290     * @param {string} the group ID
19291     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19292     */
19293     get: function(groupId) {
19294         if (typeof(this.groups[groupId]) == 'undefined') {
19295             return false;
19296         }
19297         
19298         return this.groups[groupId] ;
19299     }
19300     
19301     
19302 });
19303 /*
19304  * - LGPL
19305  *
19306  * Radio
19307  *
19308  *
19309  * not inline
19310  *<div class="radio">
19311   <label>
19312     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19313     Option one is this and that&mdash;be sure to include why it's great
19314   </label>
19315 </div>
19316  *
19317  *
19318  *inline
19319  *<span>
19320  *<label class="radio-inline">fieldLabel</label>
19321  *<label class="radio-inline">
19322   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19323 </label>
19324 <span>
19325  * 
19326  * 
19327  */
19328
19329 /**
19330  * @class Roo.bootstrap.Radio
19331  * @extends Roo.bootstrap.CheckBox
19332  * Bootstrap Radio class
19333
19334  * @constructor
19335  * Create a new Radio
19336  * @param {Object} config The config object
19337  */
19338
19339 Roo.bootstrap.Radio = function(config){
19340     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19341    
19342 };
19343
19344 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19345     
19346     inputType: 'radio',
19347     inputValue: '',
19348     valueOff: '',
19349     
19350     getAutoCreate : function()
19351     {
19352         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19353         align = align || 'left'; // default...
19354         
19355         
19356         
19357         var id = Roo.id();
19358         
19359         var cfg = {
19360                 tag : this.inline ? 'span' : 'div',
19361                 cls : '',
19362                 cn : []
19363         };
19364         
19365         var inline = this.inline ? ' radio-inline' : '';
19366         
19367         var lbl = {
19368                 tag: 'label' ,
19369                 // does not need for, as we wrap the input with it..
19370                 'for' : id,
19371                 cls : 'control-label box-label' + inline,
19372                 cn : []
19373         };
19374         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19375         
19376         var fieldLabel = {
19377             tag: 'label' ,
19378             //cls : 'control-label' + inline,
19379             html : this.fieldLabel,
19380             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19381         };
19382         
19383  
19384         
19385         
19386         var input =  {
19387             tag: 'input',
19388             id : id,
19389             type : this.inputType,
19390             //value : (!this.checked) ? this.valueOff : this.inputValue,
19391             value : this.inputValue,
19392             cls : 'roo-radio',
19393             placeholder : this.placeholder || '' // ?? needed????
19394             
19395         };
19396         if (this.weight) { // Validity check?
19397             input.cls += " radio-" + this.weight;
19398         }
19399         if (this.disabled) {
19400             input.disabled=true;
19401         }
19402         
19403         if(this.checked){
19404             input.checked = this.checked;
19405         }
19406         
19407         if (this.name) {
19408             input.name = this.name;
19409         }
19410         
19411         if (this.size) {
19412             input.cls += ' input-' + this.size;
19413         }
19414         
19415         //?? can span's inline have a width??
19416         
19417         var settings=this;
19418         ['xs','sm','md','lg'].map(function(size){
19419             if (settings[size]) {
19420                 cfg.cls += ' col-' + size + '-' + settings[size];
19421             }
19422         });
19423         
19424         var inputblock = input;
19425         
19426         if (this.before || this.after) {
19427             
19428             inputblock = {
19429                 cls : 'input-group',
19430                 tag : 'span',
19431                 cn :  [] 
19432             };
19433             if (this.before) {
19434                 inputblock.cn.push({
19435                     tag :'span',
19436                     cls : 'input-group-addon',
19437                     html : this.before
19438                 });
19439             }
19440             inputblock.cn.push(input);
19441             if (this.after) {
19442                 inputblock.cn.push({
19443                     tag :'span',
19444                     cls : 'input-group-addon',
19445                     html : this.after
19446                 });
19447             }
19448             
19449         };
19450         
19451         
19452         if (this.fieldLabel && this.fieldLabel.length) {
19453             cfg.cn.push(fieldLabel);
19454         }
19455        
19456         // normal bootstrap puts the input inside the label.
19457         // however with our styled version - it has to go after the input.
19458        
19459         //lbl.cn.push(inputblock);
19460         
19461         var lblwrap =  {
19462             tag: 'span',
19463             cls: 'radio' + inline,
19464             cn: [
19465                 inputblock,
19466                 lbl
19467             ]
19468         };
19469         
19470         cfg.cn.push( lblwrap);
19471         
19472         if(this.boxLabel){
19473             lbl.cn.push({
19474                 tag: 'span',
19475                 html: this.boxLabel
19476             })
19477         }
19478          
19479         
19480         return cfg;
19481         
19482     },
19483     
19484     initEvents : function()
19485     {
19486 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19487         
19488         this.inputEl().on('click', this.onClick,  this);
19489         if (this.boxLabel) {
19490             //Roo.log('find label');
19491             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19492         }
19493         
19494     },
19495     
19496     inputEl: function ()
19497     {
19498         return this.el.select('input.roo-radio',true).first();
19499     },
19500     onClick : function()
19501     {   
19502         Roo.log("click");
19503         this.setChecked(true);
19504     },
19505     
19506     setChecked : function(state,suppressEvent)
19507     {
19508         if(state){
19509             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19510                 v.dom.checked = false;
19511             });
19512         }
19513         Roo.log(this.inputEl().dom);
19514         this.checked = state;
19515         this.inputEl().dom.checked = state;
19516         
19517         if(suppressEvent !== true){
19518             this.fireEvent('check', this, state);
19519         }
19520         
19521         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19522         
19523     },
19524     
19525     getGroupValue : function()
19526     {
19527         var value = '';
19528         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19529             if(v.dom.checked == true){
19530                 value = v.dom.value;
19531             }
19532         });
19533         
19534         return value;
19535     },
19536     
19537     /**
19538      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19539      * @return {Mixed} value The field value
19540      */
19541     getValue : function(){
19542         return this.getGroupValue();
19543     }
19544     
19545 });
19546
19547  
19548 //<script type="text/javascript">
19549
19550 /*
19551  * Based  Ext JS Library 1.1.1
19552  * Copyright(c) 2006-2007, Ext JS, LLC.
19553  * LGPL
19554  *
19555  */
19556  
19557 /**
19558  * @class Roo.HtmlEditorCore
19559  * @extends Roo.Component
19560  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19561  *
19562  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19563  */
19564
19565 Roo.HtmlEditorCore = function(config){
19566     
19567     
19568     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19569     
19570     
19571     this.addEvents({
19572         /**
19573          * @event initialize
19574          * Fires when the editor is fully initialized (including the iframe)
19575          * @param {Roo.HtmlEditorCore} this
19576          */
19577         initialize: true,
19578         /**
19579          * @event activate
19580          * Fires when the editor is first receives the focus. Any insertion must wait
19581          * until after this event.
19582          * @param {Roo.HtmlEditorCore} this
19583          */
19584         activate: true,
19585          /**
19586          * @event beforesync
19587          * Fires before the textarea is updated with content from the editor iframe. Return false
19588          * to cancel the sync.
19589          * @param {Roo.HtmlEditorCore} this
19590          * @param {String} html
19591          */
19592         beforesync: true,
19593          /**
19594          * @event beforepush
19595          * Fires before the iframe editor is updated with content from the textarea. Return false
19596          * to cancel the push.
19597          * @param {Roo.HtmlEditorCore} this
19598          * @param {String} html
19599          */
19600         beforepush: true,
19601          /**
19602          * @event sync
19603          * Fires when the textarea is updated with content from the editor iframe.
19604          * @param {Roo.HtmlEditorCore} this
19605          * @param {String} html
19606          */
19607         sync: true,
19608          /**
19609          * @event push
19610          * Fires when the iframe editor is updated with content from the textarea.
19611          * @param {Roo.HtmlEditorCore} this
19612          * @param {String} html
19613          */
19614         push: true,
19615         
19616         /**
19617          * @event editorevent
19618          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19619          * @param {Roo.HtmlEditorCore} this
19620          */
19621         editorevent: true
19622         
19623     });
19624     
19625     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19626     
19627     // defaults : white / black...
19628     this.applyBlacklists();
19629     
19630     
19631     
19632 };
19633
19634
19635 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19636
19637
19638      /**
19639      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19640      */
19641     
19642     owner : false,
19643     
19644      /**
19645      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19646      *                        Roo.resizable.
19647      */
19648     resizable : false,
19649      /**
19650      * @cfg {Number} height (in pixels)
19651      */   
19652     height: 300,
19653    /**
19654      * @cfg {Number} width (in pixels)
19655      */   
19656     width: 500,
19657     
19658     /**
19659      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19660      * 
19661      */
19662     stylesheets: false,
19663     
19664     // id of frame..
19665     frameId: false,
19666     
19667     // private properties
19668     validationEvent : false,
19669     deferHeight: true,
19670     initialized : false,
19671     activated : false,
19672     sourceEditMode : false,
19673     onFocus : Roo.emptyFn,
19674     iframePad:3,
19675     hideMode:'offsets',
19676     
19677     clearUp: true,
19678     
19679     // blacklist + whitelisted elements..
19680     black: false,
19681     white: false,
19682      
19683     
19684
19685     /**
19686      * Protected method that will not generally be called directly. It
19687      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19688      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19689      */
19690     getDocMarkup : function(){
19691         // body styles..
19692         var st = '';
19693         
19694         // inherit styels from page...?? 
19695         if (this.stylesheets === false) {
19696             
19697             Roo.get(document.head).select('style').each(function(node) {
19698                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19699             });
19700             
19701             Roo.get(document.head).select('link').each(function(node) { 
19702                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19703             });
19704             
19705         } else if (!this.stylesheets.length) {
19706                 // simple..
19707                 st = '<style type="text/css">' +
19708                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19709                    '</style>';
19710         } else { 
19711             
19712         }
19713         
19714         st +=  '<style type="text/css">' +
19715             'IMG { cursor: pointer } ' +
19716         '</style>';
19717
19718         
19719         return '<html><head>' + st  +
19720             //<style type="text/css">' +
19721             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19722             //'</style>' +
19723             ' </head><body class="roo-htmleditor-body"></body></html>';
19724     },
19725
19726     // private
19727     onRender : function(ct, position)
19728     {
19729         var _t = this;
19730         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19731         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19732         
19733         
19734         this.el.dom.style.border = '0 none';
19735         this.el.dom.setAttribute('tabIndex', -1);
19736         this.el.addClass('x-hidden hide');
19737         
19738         
19739         
19740         if(Roo.isIE){ // fix IE 1px bogus margin
19741             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19742         }
19743        
19744         
19745         this.frameId = Roo.id();
19746         
19747          
19748         
19749         var iframe = this.owner.wrap.createChild({
19750             tag: 'iframe',
19751             cls: 'form-control', // bootstrap..
19752             id: this.frameId,
19753             name: this.frameId,
19754             frameBorder : 'no',
19755             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19756         }, this.el
19757         );
19758         
19759         
19760         this.iframe = iframe.dom;
19761
19762          this.assignDocWin();
19763         
19764         this.doc.designMode = 'on';
19765        
19766         this.doc.open();
19767         this.doc.write(this.getDocMarkup());
19768         this.doc.close();
19769
19770         
19771         var task = { // must defer to wait for browser to be ready
19772             run : function(){
19773                 //console.log("run task?" + this.doc.readyState);
19774                 this.assignDocWin();
19775                 if(this.doc.body || this.doc.readyState == 'complete'){
19776                     try {
19777                         this.doc.designMode="on";
19778                     } catch (e) {
19779                         return;
19780                     }
19781                     Roo.TaskMgr.stop(task);
19782                     this.initEditor.defer(10, this);
19783                 }
19784             },
19785             interval : 10,
19786             duration: 10000,
19787             scope: this
19788         };
19789         Roo.TaskMgr.start(task);
19790
19791     },
19792
19793     // private
19794     onResize : function(w, h)
19795     {
19796          Roo.log('resize: ' +w + ',' + h );
19797         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19798         if(!this.iframe){
19799             return;
19800         }
19801         if(typeof w == 'number'){
19802             
19803             this.iframe.style.width = w + 'px';
19804         }
19805         if(typeof h == 'number'){
19806             
19807             this.iframe.style.height = h + 'px';
19808             if(this.doc){
19809                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19810             }
19811         }
19812         
19813     },
19814
19815     /**
19816      * Toggles the editor between standard and source edit mode.
19817      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19818      */
19819     toggleSourceEdit : function(sourceEditMode){
19820         
19821         this.sourceEditMode = sourceEditMode === true;
19822         
19823         if(this.sourceEditMode){
19824  
19825             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19826             
19827         }else{
19828             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19829             //this.iframe.className = '';
19830             this.deferFocus();
19831         }
19832         //this.setSize(this.owner.wrap.getSize());
19833         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19834     },
19835
19836     
19837   
19838
19839     /**
19840      * Protected method that will not generally be called directly. If you need/want
19841      * custom HTML cleanup, this is the method you should override.
19842      * @param {String} html The HTML to be cleaned
19843      * return {String} The cleaned HTML
19844      */
19845     cleanHtml : function(html){
19846         html = String(html);
19847         if(html.length > 5){
19848             if(Roo.isSafari){ // strip safari nonsense
19849                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19850             }
19851         }
19852         if(html == '&nbsp;'){
19853             html = '';
19854         }
19855         return html;
19856     },
19857
19858     /**
19859      * HTML Editor -> Textarea
19860      * Protected method that will not generally be called directly. Syncs the contents
19861      * of the editor iframe with the textarea.
19862      */
19863     syncValue : function(){
19864         if(this.initialized){
19865             var bd = (this.doc.body || this.doc.documentElement);
19866             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19867             var html = bd.innerHTML;
19868             if(Roo.isSafari){
19869                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19870                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19871                 if(m && m[1]){
19872                     html = '<div style="'+m[0]+'">' + html + '</div>';
19873                 }
19874             }
19875             html = this.cleanHtml(html);
19876             // fix up the special chars.. normaly like back quotes in word...
19877             // however we do not want to do this with chinese..
19878             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19879                 var cc = b.charCodeAt();
19880                 if (
19881                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19882                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19883                     (cc >= 0xf900 && cc < 0xfb00 )
19884                 ) {
19885                         return b;
19886                 }
19887                 return "&#"+cc+";" 
19888             });
19889             if(this.owner.fireEvent('beforesync', this, html) !== false){
19890                 this.el.dom.value = html;
19891                 this.owner.fireEvent('sync', this, html);
19892             }
19893         }
19894     },
19895
19896     /**
19897      * Protected method that will not generally be called directly. Pushes the value of the textarea
19898      * into the iframe editor.
19899      */
19900     pushValue : function(){
19901         if(this.initialized){
19902             var v = this.el.dom.value.trim();
19903             
19904 //            if(v.length < 1){
19905 //                v = '&#160;';
19906 //            }
19907             
19908             if(this.owner.fireEvent('beforepush', this, v) !== false){
19909                 var d = (this.doc.body || this.doc.documentElement);
19910                 d.innerHTML = v;
19911                 this.cleanUpPaste();
19912                 this.el.dom.value = d.innerHTML;
19913                 this.owner.fireEvent('push', this, v);
19914             }
19915         }
19916     },
19917
19918     // private
19919     deferFocus : function(){
19920         this.focus.defer(10, this);
19921     },
19922
19923     // doc'ed in Field
19924     focus : function(){
19925         if(this.win && !this.sourceEditMode){
19926             this.win.focus();
19927         }else{
19928             this.el.focus();
19929         }
19930     },
19931     
19932     assignDocWin: function()
19933     {
19934         var iframe = this.iframe;
19935         
19936          if(Roo.isIE){
19937             this.doc = iframe.contentWindow.document;
19938             this.win = iframe.contentWindow;
19939         } else {
19940 //            if (!Roo.get(this.frameId)) {
19941 //                return;
19942 //            }
19943 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19944 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19945             
19946             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19947                 return;
19948             }
19949             
19950             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19951             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19952         }
19953     },
19954     
19955     // private
19956     initEditor : function(){
19957         //console.log("INIT EDITOR");
19958         this.assignDocWin();
19959         
19960         
19961         
19962         this.doc.designMode="on";
19963         this.doc.open();
19964         this.doc.write(this.getDocMarkup());
19965         this.doc.close();
19966         
19967         var dbody = (this.doc.body || this.doc.documentElement);
19968         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19969         // this copies styles from the containing element into thsi one..
19970         // not sure why we need all of this..
19971         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19972         
19973         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19974         //ss['background-attachment'] = 'fixed'; // w3c
19975         dbody.bgProperties = 'fixed'; // ie
19976         //Roo.DomHelper.applyStyles(dbody, ss);
19977         Roo.EventManager.on(this.doc, {
19978             //'mousedown': this.onEditorEvent,
19979             'mouseup': this.onEditorEvent,
19980             'dblclick': this.onEditorEvent,
19981             'click': this.onEditorEvent,
19982             'keyup': this.onEditorEvent,
19983             buffer:100,
19984             scope: this
19985         });
19986         if(Roo.isGecko){
19987             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19988         }
19989         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19990             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19991         }
19992         this.initialized = true;
19993
19994         this.owner.fireEvent('initialize', this);
19995         this.pushValue();
19996     },
19997
19998     // private
19999     onDestroy : function(){
20000         
20001         
20002         
20003         if(this.rendered){
20004             
20005             //for (var i =0; i < this.toolbars.length;i++) {
20006             //    // fixme - ask toolbars for heights?
20007             //    this.toolbars[i].onDestroy();
20008            // }
20009             
20010             //this.wrap.dom.innerHTML = '';
20011             //this.wrap.remove();
20012         }
20013     },
20014
20015     // private
20016     onFirstFocus : function(){
20017         
20018         this.assignDocWin();
20019         
20020         
20021         this.activated = true;
20022          
20023     
20024         if(Roo.isGecko){ // prevent silly gecko errors
20025             this.win.focus();
20026             var s = this.win.getSelection();
20027             if(!s.focusNode || s.focusNode.nodeType != 3){
20028                 var r = s.getRangeAt(0);
20029                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20030                 r.collapse(true);
20031                 this.deferFocus();
20032             }
20033             try{
20034                 this.execCmd('useCSS', true);
20035                 this.execCmd('styleWithCSS', false);
20036             }catch(e){}
20037         }
20038         this.owner.fireEvent('activate', this);
20039     },
20040
20041     // private
20042     adjustFont: function(btn){
20043         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20044         //if(Roo.isSafari){ // safari
20045         //    adjust *= 2;
20046        // }
20047         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20048         if(Roo.isSafari){ // safari
20049             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20050             v =  (v < 10) ? 10 : v;
20051             v =  (v > 48) ? 48 : v;
20052             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20053             
20054         }
20055         
20056         
20057         v = Math.max(1, v+adjust);
20058         
20059         this.execCmd('FontSize', v  );
20060     },
20061
20062     onEditorEvent : function(e)
20063     {
20064         this.owner.fireEvent('editorevent', this, e);
20065       //  this.updateToolbar();
20066         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20067     },
20068
20069     insertTag : function(tg)
20070     {
20071         // could be a bit smarter... -> wrap the current selected tRoo..
20072         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20073             
20074             range = this.createRange(this.getSelection());
20075             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20076             wrappingNode.appendChild(range.extractContents());
20077             range.insertNode(wrappingNode);
20078
20079             return;
20080             
20081             
20082             
20083         }
20084         this.execCmd("formatblock",   tg);
20085         
20086     },
20087     
20088     insertText : function(txt)
20089     {
20090         
20091         
20092         var range = this.createRange();
20093         range.deleteContents();
20094                //alert(Sender.getAttribute('label'));
20095                
20096         range.insertNode(this.doc.createTextNode(txt));
20097     } ,
20098     
20099      
20100
20101     /**
20102      * Executes a Midas editor command on the editor document and performs necessary focus and
20103      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20104      * @param {String} cmd The Midas command
20105      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20106      */
20107     relayCmd : function(cmd, value){
20108         this.win.focus();
20109         this.execCmd(cmd, value);
20110         this.owner.fireEvent('editorevent', this);
20111         //this.updateToolbar();
20112         this.owner.deferFocus();
20113     },
20114
20115     /**
20116      * Executes a Midas editor command directly on the editor document.
20117      * For visual commands, you should use {@link #relayCmd} instead.
20118      * <b>This should only be called after the editor is initialized.</b>
20119      * @param {String} cmd The Midas command
20120      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20121      */
20122     execCmd : function(cmd, value){
20123         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20124         this.syncValue();
20125     },
20126  
20127  
20128    
20129     /**
20130      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20131      * to insert tRoo.
20132      * @param {String} text | dom node.. 
20133      */
20134     insertAtCursor : function(text)
20135     {
20136         
20137         
20138         
20139         if(!this.activated){
20140             return;
20141         }
20142         /*
20143         if(Roo.isIE){
20144             this.win.focus();
20145             var r = this.doc.selection.createRange();
20146             if(r){
20147                 r.collapse(true);
20148                 r.pasteHTML(text);
20149                 this.syncValue();
20150                 this.deferFocus();
20151             
20152             }
20153             return;
20154         }
20155         */
20156         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20157             this.win.focus();
20158             
20159             
20160             // from jquery ui (MIT licenced)
20161             var range, node;
20162             var win = this.win;
20163             
20164             if (win.getSelection && win.getSelection().getRangeAt) {
20165                 range = win.getSelection().getRangeAt(0);
20166                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20167                 range.insertNode(node);
20168             } else if (win.document.selection && win.document.selection.createRange) {
20169                 // no firefox support
20170                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20171                 win.document.selection.createRange().pasteHTML(txt);
20172             } else {
20173                 // no firefox support
20174                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20175                 this.execCmd('InsertHTML', txt);
20176             } 
20177             
20178             this.syncValue();
20179             
20180             this.deferFocus();
20181         }
20182     },
20183  // private
20184     mozKeyPress : function(e){
20185         if(e.ctrlKey){
20186             var c = e.getCharCode(), cmd;
20187           
20188             if(c > 0){
20189                 c = String.fromCharCode(c).toLowerCase();
20190                 switch(c){
20191                     case 'b':
20192                         cmd = 'bold';
20193                         break;
20194                     case 'i':
20195                         cmd = 'italic';
20196                         break;
20197                     
20198                     case 'u':
20199                         cmd = 'underline';
20200                         break;
20201                     
20202                     case 'v':
20203                         this.cleanUpPaste.defer(100, this);
20204                         return;
20205                         
20206                 }
20207                 if(cmd){
20208                     this.win.focus();
20209                     this.execCmd(cmd);
20210                     this.deferFocus();
20211                     e.preventDefault();
20212                 }
20213                 
20214             }
20215         }
20216     },
20217
20218     // private
20219     fixKeys : function(){ // load time branching for fastest keydown performance
20220         if(Roo.isIE){
20221             return function(e){
20222                 var k = e.getKey(), r;
20223                 if(k == e.TAB){
20224                     e.stopEvent();
20225                     r = this.doc.selection.createRange();
20226                     if(r){
20227                         r.collapse(true);
20228                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20229                         this.deferFocus();
20230                     }
20231                     return;
20232                 }
20233                 
20234                 if(k == e.ENTER){
20235                     r = this.doc.selection.createRange();
20236                     if(r){
20237                         var target = r.parentElement();
20238                         if(!target || target.tagName.toLowerCase() != 'li'){
20239                             e.stopEvent();
20240                             r.pasteHTML('<br />');
20241                             r.collapse(false);
20242                             r.select();
20243                         }
20244                     }
20245                 }
20246                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20247                     this.cleanUpPaste.defer(100, this);
20248                     return;
20249                 }
20250                 
20251                 
20252             };
20253         }else if(Roo.isOpera){
20254             return function(e){
20255                 var k = e.getKey();
20256                 if(k == e.TAB){
20257                     e.stopEvent();
20258                     this.win.focus();
20259                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20260                     this.deferFocus();
20261                 }
20262                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20263                     this.cleanUpPaste.defer(100, this);
20264                     return;
20265                 }
20266                 
20267             };
20268         }else if(Roo.isSafari){
20269             return function(e){
20270                 var k = e.getKey();
20271                 
20272                 if(k == e.TAB){
20273                     e.stopEvent();
20274                     this.execCmd('InsertText','\t');
20275                     this.deferFocus();
20276                     return;
20277                 }
20278                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20279                     this.cleanUpPaste.defer(100, this);
20280                     return;
20281                 }
20282                 
20283              };
20284         }
20285     }(),
20286     
20287     getAllAncestors: function()
20288     {
20289         var p = this.getSelectedNode();
20290         var a = [];
20291         if (!p) {
20292             a.push(p); // push blank onto stack..
20293             p = this.getParentElement();
20294         }
20295         
20296         
20297         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20298             a.push(p);
20299             p = p.parentNode;
20300         }
20301         a.push(this.doc.body);
20302         return a;
20303     },
20304     lastSel : false,
20305     lastSelNode : false,
20306     
20307     
20308     getSelection : function() 
20309     {
20310         this.assignDocWin();
20311         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20312     },
20313     
20314     getSelectedNode: function() 
20315     {
20316         // this may only work on Gecko!!!
20317         
20318         // should we cache this!!!!
20319         
20320         
20321         
20322          
20323         var range = this.createRange(this.getSelection()).cloneRange();
20324         
20325         if (Roo.isIE) {
20326             var parent = range.parentElement();
20327             while (true) {
20328                 var testRange = range.duplicate();
20329                 testRange.moveToElementText(parent);
20330                 if (testRange.inRange(range)) {
20331                     break;
20332                 }
20333                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20334                     break;
20335                 }
20336                 parent = parent.parentElement;
20337             }
20338             return parent;
20339         }
20340         
20341         // is ancestor a text element.
20342         var ac =  range.commonAncestorContainer;
20343         if (ac.nodeType == 3) {
20344             ac = ac.parentNode;
20345         }
20346         
20347         var ar = ac.childNodes;
20348          
20349         var nodes = [];
20350         var other_nodes = [];
20351         var has_other_nodes = false;
20352         for (var i=0;i<ar.length;i++) {
20353             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20354                 continue;
20355             }
20356             // fullly contained node.
20357             
20358             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20359                 nodes.push(ar[i]);
20360                 continue;
20361             }
20362             
20363             // probably selected..
20364             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20365                 other_nodes.push(ar[i]);
20366                 continue;
20367             }
20368             // outer..
20369             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20370                 continue;
20371             }
20372             
20373             
20374             has_other_nodes = true;
20375         }
20376         if (!nodes.length && other_nodes.length) {
20377             nodes= other_nodes;
20378         }
20379         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20380             return false;
20381         }
20382         
20383         return nodes[0];
20384     },
20385     createRange: function(sel)
20386     {
20387         // this has strange effects when using with 
20388         // top toolbar - not sure if it's a great idea.
20389         //this.editor.contentWindow.focus();
20390         if (typeof sel != "undefined") {
20391             try {
20392                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20393             } catch(e) {
20394                 return this.doc.createRange();
20395             }
20396         } else {
20397             return this.doc.createRange();
20398         }
20399     },
20400     getParentElement: function()
20401     {
20402         
20403         this.assignDocWin();
20404         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20405         
20406         var range = this.createRange(sel);
20407          
20408         try {
20409             var p = range.commonAncestorContainer;
20410             while (p.nodeType == 3) { // text node
20411                 p = p.parentNode;
20412             }
20413             return p;
20414         } catch (e) {
20415             return null;
20416         }
20417     
20418     },
20419     /***
20420      *
20421      * Range intersection.. the hard stuff...
20422      *  '-1' = before
20423      *  '0' = hits..
20424      *  '1' = after.
20425      *         [ -- selected range --- ]
20426      *   [fail]                        [fail]
20427      *
20428      *    basically..
20429      *      if end is before start or  hits it. fail.
20430      *      if start is after end or hits it fail.
20431      *
20432      *   if either hits (but other is outside. - then it's not 
20433      *   
20434      *    
20435      **/
20436     
20437     
20438     // @see http://www.thismuchiknow.co.uk/?p=64.
20439     rangeIntersectsNode : function(range, node)
20440     {
20441         var nodeRange = node.ownerDocument.createRange();
20442         try {
20443             nodeRange.selectNode(node);
20444         } catch (e) {
20445             nodeRange.selectNodeContents(node);
20446         }
20447     
20448         var rangeStartRange = range.cloneRange();
20449         rangeStartRange.collapse(true);
20450     
20451         var rangeEndRange = range.cloneRange();
20452         rangeEndRange.collapse(false);
20453     
20454         var nodeStartRange = nodeRange.cloneRange();
20455         nodeStartRange.collapse(true);
20456     
20457         var nodeEndRange = nodeRange.cloneRange();
20458         nodeEndRange.collapse(false);
20459     
20460         return rangeStartRange.compareBoundaryPoints(
20461                  Range.START_TO_START, nodeEndRange) == -1 &&
20462                rangeEndRange.compareBoundaryPoints(
20463                  Range.START_TO_START, nodeStartRange) == 1;
20464         
20465          
20466     },
20467     rangeCompareNode : function(range, node)
20468     {
20469         var nodeRange = node.ownerDocument.createRange();
20470         try {
20471             nodeRange.selectNode(node);
20472         } catch (e) {
20473             nodeRange.selectNodeContents(node);
20474         }
20475         
20476         
20477         range.collapse(true);
20478     
20479         nodeRange.collapse(true);
20480      
20481         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20482         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20483          
20484         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20485         
20486         var nodeIsBefore   =  ss == 1;
20487         var nodeIsAfter    = ee == -1;
20488         
20489         if (nodeIsBefore && nodeIsAfter) {
20490             return 0; // outer
20491         }
20492         if (!nodeIsBefore && nodeIsAfter) {
20493             return 1; //right trailed.
20494         }
20495         
20496         if (nodeIsBefore && !nodeIsAfter) {
20497             return 2;  // left trailed.
20498         }
20499         // fully contined.
20500         return 3;
20501     },
20502
20503     // private? - in a new class?
20504     cleanUpPaste :  function()
20505     {
20506         // cleans up the whole document..
20507         Roo.log('cleanuppaste');
20508         
20509         this.cleanUpChildren(this.doc.body);
20510         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20511         if (clean != this.doc.body.innerHTML) {
20512             this.doc.body.innerHTML = clean;
20513         }
20514         
20515     },
20516     
20517     cleanWordChars : function(input) {// change the chars to hex code
20518         var he = Roo.HtmlEditorCore;
20519         
20520         var output = input;
20521         Roo.each(he.swapCodes, function(sw) { 
20522             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20523             
20524             output = output.replace(swapper, sw[1]);
20525         });
20526         
20527         return output;
20528     },
20529     
20530     
20531     cleanUpChildren : function (n)
20532     {
20533         if (!n.childNodes.length) {
20534             return;
20535         }
20536         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20537            this.cleanUpChild(n.childNodes[i]);
20538         }
20539     },
20540     
20541     
20542         
20543     
20544     cleanUpChild : function (node)
20545     {
20546         var ed = this;
20547         //console.log(node);
20548         if (node.nodeName == "#text") {
20549             // clean up silly Windows -- stuff?
20550             return; 
20551         }
20552         if (node.nodeName == "#comment") {
20553             node.parentNode.removeChild(node);
20554             // clean up silly Windows -- stuff?
20555             return; 
20556         }
20557         var lcname = node.tagName.toLowerCase();
20558         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20559         // whitelist of tags..
20560         
20561         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20562             // remove node.
20563             node.parentNode.removeChild(node);
20564             return;
20565             
20566         }
20567         
20568         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20569         
20570         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20571         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20572         
20573         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20574         //    remove_keep_children = true;
20575         //}
20576         
20577         if (remove_keep_children) {
20578             this.cleanUpChildren(node);
20579             // inserts everything just before this node...
20580             while (node.childNodes.length) {
20581                 var cn = node.childNodes[0];
20582                 node.removeChild(cn);
20583                 node.parentNode.insertBefore(cn, node);
20584             }
20585             node.parentNode.removeChild(node);
20586             return;
20587         }
20588         
20589         if (!node.attributes || !node.attributes.length) {
20590             this.cleanUpChildren(node);
20591             return;
20592         }
20593         
20594         function cleanAttr(n,v)
20595         {
20596             
20597             if (v.match(/^\./) || v.match(/^\//)) {
20598                 return;
20599             }
20600             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20601                 return;
20602             }
20603             if (v.match(/^#/)) {
20604                 return;
20605             }
20606 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20607             node.removeAttribute(n);
20608             
20609         }
20610         
20611         var cwhite = this.cwhite;
20612         var cblack = this.cblack;
20613             
20614         function cleanStyle(n,v)
20615         {
20616             if (v.match(/expression/)) { //XSS?? should we even bother..
20617                 node.removeAttribute(n);
20618                 return;
20619             }
20620             
20621             var parts = v.split(/;/);
20622             var clean = [];
20623             
20624             Roo.each(parts, function(p) {
20625                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20626                 if (!p.length) {
20627                     return true;
20628                 }
20629                 var l = p.split(':').shift().replace(/\s+/g,'');
20630                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20631                 
20632                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20633 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20634                     //node.removeAttribute(n);
20635                     return true;
20636                 }
20637                 //Roo.log()
20638                 // only allow 'c whitelisted system attributes'
20639                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20640 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20641                     //node.removeAttribute(n);
20642                     return true;
20643                 }
20644                 
20645                 
20646                  
20647                 
20648                 clean.push(p);
20649                 return true;
20650             });
20651             if (clean.length) { 
20652                 node.setAttribute(n, clean.join(';'));
20653             } else {
20654                 node.removeAttribute(n);
20655             }
20656             
20657         }
20658         
20659         
20660         for (var i = node.attributes.length-1; i > -1 ; i--) {
20661             var a = node.attributes[i];
20662             //console.log(a);
20663             
20664             if (a.name.toLowerCase().substr(0,2)=='on')  {
20665                 node.removeAttribute(a.name);
20666                 continue;
20667             }
20668             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20669                 node.removeAttribute(a.name);
20670                 continue;
20671             }
20672             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20673                 cleanAttr(a.name,a.value); // fixme..
20674                 continue;
20675             }
20676             if (a.name == 'style') {
20677                 cleanStyle(a.name,a.value);
20678                 continue;
20679             }
20680             /// clean up MS crap..
20681             // tecnically this should be a list of valid class'es..
20682             
20683             
20684             if (a.name == 'class') {
20685                 if (a.value.match(/^Mso/)) {
20686                     node.className = '';
20687                 }
20688                 
20689                 if (a.value.match(/body/)) {
20690                     node.className = '';
20691                 }
20692                 continue;
20693             }
20694             
20695             // style cleanup!?
20696             // class cleanup?
20697             
20698         }
20699         
20700         
20701         this.cleanUpChildren(node);
20702         
20703         
20704     },
20705     
20706     /**
20707      * Clean up MS wordisms...
20708      */
20709     cleanWord : function(node)
20710     {
20711         
20712         
20713         if (!node) {
20714             this.cleanWord(this.doc.body);
20715             return;
20716         }
20717         if (node.nodeName == "#text") {
20718             // clean up silly Windows -- stuff?
20719             return; 
20720         }
20721         if (node.nodeName == "#comment") {
20722             node.parentNode.removeChild(node);
20723             // clean up silly Windows -- stuff?
20724             return; 
20725         }
20726         
20727         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20728             node.parentNode.removeChild(node);
20729             return;
20730         }
20731         
20732         // remove - but keep children..
20733         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20734             while (node.childNodes.length) {
20735                 var cn = node.childNodes[0];
20736                 node.removeChild(cn);
20737                 node.parentNode.insertBefore(cn, node);
20738             }
20739             node.parentNode.removeChild(node);
20740             this.iterateChildren(node, this.cleanWord);
20741             return;
20742         }
20743         // clean styles
20744         if (node.className.length) {
20745             
20746             var cn = node.className.split(/\W+/);
20747             var cna = [];
20748             Roo.each(cn, function(cls) {
20749                 if (cls.match(/Mso[a-zA-Z]+/)) {
20750                     return;
20751                 }
20752                 cna.push(cls);
20753             });
20754             node.className = cna.length ? cna.join(' ') : '';
20755             if (!cna.length) {
20756                 node.removeAttribute("class");
20757             }
20758         }
20759         
20760         if (node.hasAttribute("lang")) {
20761             node.removeAttribute("lang");
20762         }
20763         
20764         if (node.hasAttribute("style")) {
20765             
20766             var styles = node.getAttribute("style").split(";");
20767             var nstyle = [];
20768             Roo.each(styles, function(s) {
20769                 if (!s.match(/:/)) {
20770                     return;
20771                 }
20772                 var kv = s.split(":");
20773                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20774                     return;
20775                 }
20776                 // what ever is left... we allow.
20777                 nstyle.push(s);
20778             });
20779             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20780             if (!nstyle.length) {
20781                 node.removeAttribute('style');
20782             }
20783         }
20784         this.iterateChildren(node, this.cleanWord);
20785         
20786         
20787         
20788     },
20789     /**
20790      * iterateChildren of a Node, calling fn each time, using this as the scole..
20791      * @param {DomNode} node node to iterate children of.
20792      * @param {Function} fn method of this class to call on each item.
20793      */
20794     iterateChildren : function(node, fn)
20795     {
20796         if (!node.childNodes.length) {
20797                 return;
20798         }
20799         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20800            fn.call(this, node.childNodes[i])
20801         }
20802     },
20803     
20804     
20805     /**
20806      * cleanTableWidths.
20807      *
20808      * Quite often pasting from word etc.. results in tables with column and widths.
20809      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20810      *
20811      */
20812     cleanTableWidths : function(node)
20813     {
20814          
20815          
20816         if (!node) {
20817             this.cleanTableWidths(this.doc.body);
20818             return;
20819         }
20820         
20821         // ignore list...
20822         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20823             return; 
20824         }
20825         Roo.log(node.tagName);
20826         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20827             this.iterateChildren(node, this.cleanTableWidths);
20828             return;
20829         }
20830         if (node.hasAttribute('width')) {
20831             node.removeAttribute('width');
20832         }
20833         
20834          
20835         if (node.hasAttribute("style")) {
20836             // pretty basic...
20837             
20838             var styles = node.getAttribute("style").split(";");
20839             var nstyle = [];
20840             Roo.each(styles, function(s) {
20841                 if (!s.match(/:/)) {
20842                     return;
20843                 }
20844                 var kv = s.split(":");
20845                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20846                     return;
20847                 }
20848                 // what ever is left... we allow.
20849                 nstyle.push(s);
20850             });
20851             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20852             if (!nstyle.length) {
20853                 node.removeAttribute('style');
20854             }
20855         }
20856         
20857         this.iterateChildren(node, this.cleanTableWidths);
20858         
20859         
20860     },
20861     
20862     
20863     
20864     
20865     domToHTML : function(currentElement, depth, nopadtext) {
20866         
20867         depth = depth || 0;
20868         nopadtext = nopadtext || false;
20869     
20870         if (!currentElement) {
20871             return this.domToHTML(this.doc.body);
20872         }
20873         
20874         //Roo.log(currentElement);
20875         var j;
20876         var allText = false;
20877         var nodeName = currentElement.nodeName;
20878         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20879         
20880         if  (nodeName == '#text') {
20881             
20882             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20883         }
20884         
20885         
20886         var ret = '';
20887         if (nodeName != 'BODY') {
20888              
20889             var i = 0;
20890             // Prints the node tagName, such as <A>, <IMG>, etc
20891             if (tagName) {
20892                 var attr = [];
20893                 for(i = 0; i < currentElement.attributes.length;i++) {
20894                     // quoting?
20895                     var aname = currentElement.attributes.item(i).name;
20896                     if (!currentElement.attributes.item(i).value.length) {
20897                         continue;
20898                     }
20899                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20900                 }
20901                 
20902                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20903             } 
20904             else {
20905                 
20906                 // eack
20907             }
20908         } else {
20909             tagName = false;
20910         }
20911         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20912             return ret;
20913         }
20914         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20915             nopadtext = true;
20916         }
20917         
20918         
20919         // Traverse the tree
20920         i = 0;
20921         var currentElementChild = currentElement.childNodes.item(i);
20922         var allText = true;
20923         var innerHTML  = '';
20924         lastnode = '';
20925         while (currentElementChild) {
20926             // Formatting code (indent the tree so it looks nice on the screen)
20927             var nopad = nopadtext;
20928             if (lastnode == 'SPAN') {
20929                 nopad  = true;
20930             }
20931             // text
20932             if  (currentElementChild.nodeName == '#text') {
20933                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20934                 toadd = nopadtext ? toadd : toadd.trim();
20935                 if (!nopad && toadd.length > 80) {
20936                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20937                 }
20938                 innerHTML  += toadd;
20939                 
20940                 i++;
20941                 currentElementChild = currentElement.childNodes.item(i);
20942                 lastNode = '';
20943                 continue;
20944             }
20945             allText = false;
20946             
20947             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20948                 
20949             // Recursively traverse the tree structure of the child node
20950             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20951             lastnode = currentElementChild.nodeName;
20952             i++;
20953             currentElementChild=currentElement.childNodes.item(i);
20954         }
20955         
20956         ret += innerHTML;
20957         
20958         if (!allText) {
20959                 // The remaining code is mostly for formatting the tree
20960             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20961         }
20962         
20963         
20964         if (tagName) {
20965             ret+= "</"+tagName+">";
20966         }
20967         return ret;
20968         
20969     },
20970         
20971     applyBlacklists : function()
20972     {
20973         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20974         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20975         
20976         this.white = [];
20977         this.black = [];
20978         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20979             if (b.indexOf(tag) > -1) {
20980                 return;
20981             }
20982             this.white.push(tag);
20983             
20984         }, this);
20985         
20986         Roo.each(w, function(tag) {
20987             if (b.indexOf(tag) > -1) {
20988                 return;
20989             }
20990             if (this.white.indexOf(tag) > -1) {
20991                 return;
20992             }
20993             this.white.push(tag);
20994             
20995         }, this);
20996         
20997         
20998         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20999             if (w.indexOf(tag) > -1) {
21000                 return;
21001             }
21002             this.black.push(tag);
21003             
21004         }, this);
21005         
21006         Roo.each(b, function(tag) {
21007             if (w.indexOf(tag) > -1) {
21008                 return;
21009             }
21010             if (this.black.indexOf(tag) > -1) {
21011                 return;
21012             }
21013             this.black.push(tag);
21014             
21015         }, this);
21016         
21017         
21018         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21019         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21020         
21021         this.cwhite = [];
21022         this.cblack = [];
21023         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21024             if (b.indexOf(tag) > -1) {
21025                 return;
21026             }
21027             this.cwhite.push(tag);
21028             
21029         }, this);
21030         
21031         Roo.each(w, function(tag) {
21032             if (b.indexOf(tag) > -1) {
21033                 return;
21034             }
21035             if (this.cwhite.indexOf(tag) > -1) {
21036                 return;
21037             }
21038             this.cwhite.push(tag);
21039             
21040         }, this);
21041         
21042         
21043         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21044             if (w.indexOf(tag) > -1) {
21045                 return;
21046             }
21047             this.cblack.push(tag);
21048             
21049         }, this);
21050         
21051         Roo.each(b, function(tag) {
21052             if (w.indexOf(tag) > -1) {
21053                 return;
21054             }
21055             if (this.cblack.indexOf(tag) > -1) {
21056                 return;
21057             }
21058             this.cblack.push(tag);
21059             
21060         }, this);
21061     },
21062     
21063     setStylesheets : function(stylesheets)
21064     {
21065         if(typeof(stylesheets) == 'string'){
21066             Roo.get(this.iframe.contentDocument.head).createChild({
21067                 tag : 'link',
21068                 rel : 'stylesheet',
21069                 type : 'text/css',
21070                 href : stylesheets
21071             });
21072             
21073             return;
21074         }
21075         var _this = this;
21076      
21077         Roo.each(stylesheets, function(s) {
21078             if(!s.length){
21079                 return;
21080             }
21081             
21082             Roo.get(_this.iframe.contentDocument.head).createChild({
21083                 tag : 'link',
21084                 rel : 'stylesheet',
21085                 type : 'text/css',
21086                 href : s
21087             });
21088         });
21089
21090         
21091     },
21092     
21093     removeStylesheets : function()
21094     {
21095         var _this = this;
21096         
21097         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21098             s.remove();
21099         });
21100     }
21101     
21102     // hide stuff that is not compatible
21103     /**
21104      * @event blur
21105      * @hide
21106      */
21107     /**
21108      * @event change
21109      * @hide
21110      */
21111     /**
21112      * @event focus
21113      * @hide
21114      */
21115     /**
21116      * @event specialkey
21117      * @hide
21118      */
21119     /**
21120      * @cfg {String} fieldClass @hide
21121      */
21122     /**
21123      * @cfg {String} focusClass @hide
21124      */
21125     /**
21126      * @cfg {String} autoCreate @hide
21127      */
21128     /**
21129      * @cfg {String} inputType @hide
21130      */
21131     /**
21132      * @cfg {String} invalidClass @hide
21133      */
21134     /**
21135      * @cfg {String} invalidText @hide
21136      */
21137     /**
21138      * @cfg {String} msgFx @hide
21139      */
21140     /**
21141      * @cfg {String} validateOnBlur @hide
21142      */
21143 });
21144
21145 Roo.HtmlEditorCore.white = [
21146         'area', 'br', 'img', 'input', 'hr', 'wbr',
21147         
21148        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21149        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21150        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21151        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21152        'table',   'ul',         'xmp', 
21153        
21154        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21155       'thead',   'tr', 
21156      
21157       'dir', 'menu', 'ol', 'ul', 'dl',
21158        
21159       'embed',  'object'
21160 ];
21161
21162
21163 Roo.HtmlEditorCore.black = [
21164     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21165         'applet', // 
21166         'base',   'basefont', 'bgsound', 'blink',  'body', 
21167         'frame',  'frameset', 'head',    'html',   'ilayer', 
21168         'iframe', 'layer',  'link',     'meta',    'object',   
21169         'script', 'style' ,'title',  'xml' // clean later..
21170 ];
21171 Roo.HtmlEditorCore.clean = [
21172     'script', 'style', 'title', 'xml'
21173 ];
21174 Roo.HtmlEditorCore.remove = [
21175     'font'
21176 ];
21177 // attributes..
21178
21179 Roo.HtmlEditorCore.ablack = [
21180     'on'
21181 ];
21182     
21183 Roo.HtmlEditorCore.aclean = [ 
21184     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21185 ];
21186
21187 // protocols..
21188 Roo.HtmlEditorCore.pwhite= [
21189         'http',  'https',  'mailto'
21190 ];
21191
21192 // white listed style attributes.
21193 Roo.HtmlEditorCore.cwhite= [
21194       //  'text-align', /// default is to allow most things..
21195       
21196          
21197 //        'font-size'//??
21198 ];
21199
21200 // black listed style attributes.
21201 Roo.HtmlEditorCore.cblack= [
21202       //  'font-size' -- this can be set by the project 
21203 ];
21204
21205
21206 Roo.HtmlEditorCore.swapCodes   =[ 
21207     [    8211, "--" ], 
21208     [    8212, "--" ], 
21209     [    8216,  "'" ],  
21210     [    8217, "'" ],  
21211     [    8220, '"' ],  
21212     [    8221, '"' ],  
21213     [    8226, "*" ],  
21214     [    8230, "..." ]
21215 ]; 
21216
21217     /*
21218  * - LGPL
21219  *
21220  * HtmlEditor
21221  * 
21222  */
21223
21224 /**
21225  * @class Roo.bootstrap.HtmlEditor
21226  * @extends Roo.bootstrap.TextArea
21227  * Bootstrap HtmlEditor class
21228
21229  * @constructor
21230  * Create a new HtmlEditor
21231  * @param {Object} config The config object
21232  */
21233
21234 Roo.bootstrap.HtmlEditor = function(config){
21235     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21236     if (!this.toolbars) {
21237         this.toolbars = [];
21238     }
21239     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21240     this.addEvents({
21241             /**
21242              * @event initialize
21243              * Fires when the editor is fully initialized (including the iframe)
21244              * @param {HtmlEditor} this
21245              */
21246             initialize: true,
21247             /**
21248              * @event activate
21249              * Fires when the editor is first receives the focus. Any insertion must wait
21250              * until after this event.
21251              * @param {HtmlEditor} this
21252              */
21253             activate: true,
21254              /**
21255              * @event beforesync
21256              * Fires before the textarea is updated with content from the editor iframe. Return false
21257              * to cancel the sync.
21258              * @param {HtmlEditor} this
21259              * @param {String} html
21260              */
21261             beforesync: true,
21262              /**
21263              * @event beforepush
21264              * Fires before the iframe editor is updated with content from the textarea. Return false
21265              * to cancel the push.
21266              * @param {HtmlEditor} this
21267              * @param {String} html
21268              */
21269             beforepush: true,
21270              /**
21271              * @event sync
21272              * Fires when the textarea is updated with content from the editor iframe.
21273              * @param {HtmlEditor} this
21274              * @param {String} html
21275              */
21276             sync: true,
21277              /**
21278              * @event push
21279              * Fires when the iframe editor is updated with content from the textarea.
21280              * @param {HtmlEditor} this
21281              * @param {String} html
21282              */
21283             push: true,
21284              /**
21285              * @event editmodechange
21286              * Fires when the editor switches edit modes
21287              * @param {HtmlEditor} this
21288              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21289              */
21290             editmodechange: true,
21291             /**
21292              * @event editorevent
21293              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21294              * @param {HtmlEditor} this
21295              */
21296             editorevent: true,
21297             /**
21298              * @event firstfocus
21299              * Fires when on first focus - needed by toolbars..
21300              * @param {HtmlEditor} this
21301              */
21302             firstfocus: true,
21303             /**
21304              * @event autosave
21305              * Auto save the htmlEditor value as a file into Events
21306              * @param {HtmlEditor} this
21307              */
21308             autosave: true,
21309             /**
21310              * @event savedpreview
21311              * preview the saved version of htmlEditor
21312              * @param {HtmlEditor} this
21313              */
21314             savedpreview: true
21315         });
21316 };
21317
21318
21319 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21320     
21321     
21322       /**
21323      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21324      */
21325     toolbars : false,
21326    
21327      /**
21328      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21329      *                        Roo.resizable.
21330      */
21331     resizable : false,
21332      /**
21333      * @cfg {Number} height (in pixels)
21334      */   
21335     height: 300,
21336    /**
21337      * @cfg {Number} width (in pixels)
21338      */   
21339     width: false,
21340     
21341     /**
21342      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21343      * 
21344      */
21345     stylesheets: false,
21346     
21347     // id of frame..
21348     frameId: false,
21349     
21350     // private properties
21351     validationEvent : false,
21352     deferHeight: true,
21353     initialized : false,
21354     activated : false,
21355     
21356     onFocus : Roo.emptyFn,
21357     iframePad:3,
21358     hideMode:'offsets',
21359     
21360     
21361     tbContainer : false,
21362     
21363     toolbarContainer :function() {
21364         return this.wrap.select('.x-html-editor-tb',true).first();
21365     },
21366
21367     /**
21368      * Protected method that will not generally be called directly. It
21369      * is called when the editor creates its toolbar. Override this method if you need to
21370      * add custom toolbar buttons.
21371      * @param {HtmlEditor} editor
21372      */
21373     createToolbar : function(){
21374         
21375         Roo.log("create toolbars");
21376         
21377         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21378         this.toolbars[0].render(this.toolbarContainer());
21379         
21380         return;
21381         
21382 //        if (!editor.toolbars || !editor.toolbars.length) {
21383 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21384 //        }
21385 //        
21386 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21387 //            editor.toolbars[i] = Roo.factory(
21388 //                    typeof(editor.toolbars[i]) == 'string' ?
21389 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21390 //                Roo.bootstrap.HtmlEditor);
21391 //            editor.toolbars[i].init(editor);
21392 //        }
21393     },
21394
21395      
21396     // private
21397     onRender : function(ct, position)
21398     {
21399        // Roo.log("Call onRender: " + this.xtype);
21400         var _t = this;
21401         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21402       
21403         this.wrap = this.inputEl().wrap({
21404             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21405         });
21406         
21407         this.editorcore.onRender(ct, position);
21408          
21409         if (this.resizable) {
21410             this.resizeEl = new Roo.Resizable(this.wrap, {
21411                 pinned : true,
21412                 wrap: true,
21413                 dynamic : true,
21414                 minHeight : this.height,
21415                 height: this.height,
21416                 handles : this.resizable,
21417                 width: this.width,
21418                 listeners : {
21419                     resize : function(r, w, h) {
21420                         _t.onResize(w,h); // -something
21421                     }
21422                 }
21423             });
21424             
21425         }
21426         this.createToolbar(this);
21427        
21428         
21429         if(!this.width && this.resizable){
21430             this.setSize(this.wrap.getSize());
21431         }
21432         if (this.resizeEl) {
21433             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21434             // should trigger onReize..
21435         }
21436         
21437     },
21438
21439     // private
21440     onResize : function(w, h)
21441     {
21442         Roo.log('resize: ' +w + ',' + h );
21443         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21444         var ew = false;
21445         var eh = false;
21446         
21447         if(this.inputEl() ){
21448             if(typeof w == 'number'){
21449                 var aw = w - this.wrap.getFrameWidth('lr');
21450                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21451                 ew = aw;
21452             }
21453             if(typeof h == 'number'){
21454                  var tbh = -11;  // fixme it needs to tool bar size!
21455                 for (var i =0; i < this.toolbars.length;i++) {
21456                     // fixme - ask toolbars for heights?
21457                     tbh += this.toolbars[i].el.getHeight();
21458                     //if (this.toolbars[i].footer) {
21459                     //    tbh += this.toolbars[i].footer.el.getHeight();
21460                     //}
21461                 }
21462               
21463                 
21464                 
21465                 
21466                 
21467                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21468                 ah -= 5; // knock a few pixes off for look..
21469                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21470                 var eh = ah;
21471             }
21472         }
21473         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21474         this.editorcore.onResize(ew,eh);
21475         
21476     },
21477
21478     /**
21479      * Toggles the editor between standard and source edit mode.
21480      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21481      */
21482     toggleSourceEdit : function(sourceEditMode)
21483     {
21484         this.editorcore.toggleSourceEdit(sourceEditMode);
21485         
21486         if(this.editorcore.sourceEditMode){
21487             Roo.log('editor - showing textarea');
21488             
21489 //            Roo.log('in');
21490 //            Roo.log(this.syncValue());
21491             this.syncValue();
21492             this.inputEl().removeClass(['hide', 'x-hidden']);
21493             this.inputEl().dom.removeAttribute('tabIndex');
21494             this.inputEl().focus();
21495         }else{
21496             Roo.log('editor - hiding textarea');
21497 //            Roo.log('out')
21498 //            Roo.log(this.pushValue()); 
21499             this.pushValue();
21500             
21501             this.inputEl().addClass(['hide', 'x-hidden']);
21502             this.inputEl().dom.setAttribute('tabIndex', -1);
21503             //this.deferFocus();
21504         }
21505          
21506         if(this.resizable){
21507             this.setSize(this.wrap.getSize());
21508         }
21509         
21510         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21511     },
21512  
21513     // private (for BoxComponent)
21514     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21515
21516     // private (for BoxComponent)
21517     getResizeEl : function(){
21518         return this.wrap;
21519     },
21520
21521     // private (for BoxComponent)
21522     getPositionEl : function(){
21523         return this.wrap;
21524     },
21525
21526     // private
21527     initEvents : function(){
21528         this.originalValue = this.getValue();
21529     },
21530
21531 //    /**
21532 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21533 //     * @method
21534 //     */
21535 //    markInvalid : Roo.emptyFn,
21536 //    /**
21537 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21538 //     * @method
21539 //     */
21540 //    clearInvalid : Roo.emptyFn,
21541
21542     setValue : function(v){
21543         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21544         this.editorcore.pushValue();
21545     },
21546
21547      
21548     // private
21549     deferFocus : function(){
21550         this.focus.defer(10, this);
21551     },
21552
21553     // doc'ed in Field
21554     focus : function(){
21555         this.editorcore.focus();
21556         
21557     },
21558       
21559
21560     // private
21561     onDestroy : function(){
21562         
21563         
21564         
21565         if(this.rendered){
21566             
21567             for (var i =0; i < this.toolbars.length;i++) {
21568                 // fixme - ask toolbars for heights?
21569                 this.toolbars[i].onDestroy();
21570             }
21571             
21572             this.wrap.dom.innerHTML = '';
21573             this.wrap.remove();
21574         }
21575     },
21576
21577     // private
21578     onFirstFocus : function(){
21579         //Roo.log("onFirstFocus");
21580         this.editorcore.onFirstFocus();
21581          for (var i =0; i < this.toolbars.length;i++) {
21582             this.toolbars[i].onFirstFocus();
21583         }
21584         
21585     },
21586     
21587     // private
21588     syncValue : function()
21589     {   
21590         this.editorcore.syncValue();
21591     },
21592     
21593     pushValue : function()
21594     {   
21595         this.editorcore.pushValue();
21596     }
21597      
21598     
21599     // hide stuff that is not compatible
21600     /**
21601      * @event blur
21602      * @hide
21603      */
21604     /**
21605      * @event change
21606      * @hide
21607      */
21608     /**
21609      * @event focus
21610      * @hide
21611      */
21612     /**
21613      * @event specialkey
21614      * @hide
21615      */
21616     /**
21617      * @cfg {String} fieldClass @hide
21618      */
21619     /**
21620      * @cfg {String} focusClass @hide
21621      */
21622     /**
21623      * @cfg {String} autoCreate @hide
21624      */
21625     /**
21626      * @cfg {String} inputType @hide
21627      */
21628     /**
21629      * @cfg {String} invalidClass @hide
21630      */
21631     /**
21632      * @cfg {String} invalidText @hide
21633      */
21634     /**
21635      * @cfg {String} msgFx @hide
21636      */
21637     /**
21638      * @cfg {String} validateOnBlur @hide
21639      */
21640 });
21641  
21642     
21643    
21644    
21645    
21646       
21647 Roo.namespace('Roo.bootstrap.htmleditor');
21648 /**
21649  * @class Roo.bootstrap.HtmlEditorToolbar1
21650  * Basic Toolbar
21651  * 
21652  * Usage:
21653  *
21654  new Roo.bootstrap.HtmlEditor({
21655     ....
21656     toolbars : [
21657         new Roo.bootstrap.HtmlEditorToolbar1({
21658             disable : { fonts: 1 , format: 1, ..., ... , ...],
21659             btns : [ .... ]
21660         })
21661     }
21662      
21663  * 
21664  * @cfg {Object} disable List of elements to disable..
21665  * @cfg {Array} btns List of additional buttons.
21666  * 
21667  * 
21668  * NEEDS Extra CSS? 
21669  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21670  */
21671  
21672 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21673 {
21674     
21675     Roo.apply(this, config);
21676     
21677     // default disabled, based on 'good practice'..
21678     this.disable = this.disable || {};
21679     Roo.applyIf(this.disable, {
21680         fontSize : true,
21681         colors : true,
21682         specialElements : true
21683     });
21684     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21685     
21686     this.editor = config.editor;
21687     this.editorcore = config.editor.editorcore;
21688     
21689     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21690     
21691     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21692     // dont call parent... till later.
21693 }
21694 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21695      
21696     bar : true,
21697     
21698     editor : false,
21699     editorcore : false,
21700     
21701     
21702     formats : [
21703         "p" ,  
21704         "h1","h2","h3","h4","h5","h6", 
21705         "pre", "code", 
21706         "abbr", "acronym", "address", "cite", "samp", "var",
21707         'div','span'
21708     ],
21709     
21710     onRender : function(ct, position)
21711     {
21712        // Roo.log("Call onRender: " + this.xtype);
21713         
21714        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21715        Roo.log(this.el);
21716        this.el.dom.style.marginBottom = '0';
21717        var _this = this;
21718        var editorcore = this.editorcore;
21719        var editor= this.editor;
21720        
21721        var children = [];
21722        var btn = function(id,cmd , toggle, handler){
21723        
21724             var  event = toggle ? 'toggle' : 'click';
21725        
21726             var a = {
21727                 size : 'sm',
21728                 xtype: 'Button',
21729                 xns: Roo.bootstrap,
21730                 glyphicon : id,
21731                 cmd : id || cmd,
21732                 enableToggle:toggle !== false,
21733                 //html : 'submit'
21734                 pressed : toggle ? false : null,
21735                 listeners : {}
21736             };
21737             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21738                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21739             };
21740             children.push(a);
21741             return a;
21742        }
21743         
21744         var style = {
21745                 xtype: 'Button',
21746                 size : 'sm',
21747                 xns: Roo.bootstrap,
21748                 glyphicon : 'font',
21749                 //html : 'submit'
21750                 menu : {
21751                     xtype: 'Menu',
21752                     xns: Roo.bootstrap,
21753                     items:  []
21754                 }
21755         };
21756         Roo.each(this.formats, function(f) {
21757             style.menu.items.push({
21758                 xtype :'MenuItem',
21759                 xns: Roo.bootstrap,
21760                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21761                 tagname : f,
21762                 listeners : {
21763                     click : function()
21764                     {
21765                         editorcore.insertTag(this.tagname);
21766                         editor.focus();
21767                     }
21768                 }
21769                 
21770             });
21771         });
21772          children.push(style);   
21773             
21774             
21775         btn('bold',false,true);
21776         btn('italic',false,true);
21777         btn('align-left', 'justifyleft',true);
21778         btn('align-center', 'justifycenter',true);
21779         btn('align-right' , 'justifyright',true);
21780         btn('link', false, false, function(btn) {
21781             //Roo.log("create link?");
21782             var url = prompt(this.createLinkText, this.defaultLinkValue);
21783             if(url && url != 'http:/'+'/'){
21784                 this.editorcore.relayCmd('createlink', url);
21785             }
21786         }),
21787         btn('list','insertunorderedlist',true);
21788         btn('pencil', false,true, function(btn){
21789                 Roo.log(this);
21790                 
21791                 this.toggleSourceEdit(btn.pressed);
21792         });
21793         /*
21794         var cog = {
21795                 xtype: 'Button',
21796                 size : 'sm',
21797                 xns: Roo.bootstrap,
21798                 glyphicon : 'cog',
21799                 //html : 'submit'
21800                 menu : {
21801                     xtype: 'Menu',
21802                     xns: Roo.bootstrap,
21803                     items:  []
21804                 }
21805         };
21806         
21807         cog.menu.items.push({
21808             xtype :'MenuItem',
21809             xns: Roo.bootstrap,
21810             html : Clean styles,
21811             tagname : f,
21812             listeners : {
21813                 click : function()
21814                 {
21815                     editorcore.insertTag(this.tagname);
21816                     editor.focus();
21817                 }
21818             }
21819             
21820         });
21821        */
21822         
21823          
21824        this.xtype = 'NavSimplebar';
21825         
21826         for(var i=0;i< children.length;i++) {
21827             
21828             this.buttons.add(this.addxtypeChild(children[i]));
21829             
21830         }
21831         
21832         editor.on('editorevent', this.updateToolbar, this);
21833     },
21834     onBtnClick : function(id)
21835     {
21836        this.editorcore.relayCmd(id);
21837        this.editorcore.focus();
21838     },
21839     
21840     /**
21841      * Protected method that will not generally be called directly. It triggers
21842      * a toolbar update by reading the markup state of the current selection in the editor.
21843      */
21844     updateToolbar: function(){
21845
21846         if(!this.editorcore.activated){
21847             this.editor.onFirstFocus(); // is this neeed?
21848             return;
21849         }
21850
21851         var btns = this.buttons; 
21852         var doc = this.editorcore.doc;
21853         btns.get('bold').setActive(doc.queryCommandState('bold'));
21854         btns.get('italic').setActive(doc.queryCommandState('italic'));
21855         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21856         
21857         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21858         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21859         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21860         
21861         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21862         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21863          /*
21864         
21865         var ans = this.editorcore.getAllAncestors();
21866         if (this.formatCombo) {
21867             
21868             
21869             var store = this.formatCombo.store;
21870             this.formatCombo.setValue("");
21871             for (var i =0; i < ans.length;i++) {
21872                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21873                     // select it..
21874                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21875                     break;
21876                 }
21877             }
21878         }
21879         
21880         
21881         
21882         // hides menus... - so this cant be on a menu...
21883         Roo.bootstrap.MenuMgr.hideAll();
21884         */
21885         Roo.bootstrap.MenuMgr.hideAll();
21886         //this.editorsyncValue();
21887     },
21888     onFirstFocus: function() {
21889         this.buttons.each(function(item){
21890            item.enable();
21891         });
21892     },
21893     toggleSourceEdit : function(sourceEditMode){
21894         
21895           
21896         if(sourceEditMode){
21897             Roo.log("disabling buttons");
21898            this.buttons.each( function(item){
21899                 if(item.cmd != 'pencil'){
21900                     item.disable();
21901                 }
21902             });
21903           
21904         }else{
21905             Roo.log("enabling buttons");
21906             if(this.editorcore.initialized){
21907                 this.buttons.each( function(item){
21908                     item.enable();
21909                 });
21910             }
21911             
21912         }
21913         Roo.log("calling toggole on editor");
21914         // tell the editor that it's been pressed..
21915         this.editor.toggleSourceEdit(sourceEditMode);
21916        
21917     }
21918 });
21919
21920
21921
21922
21923
21924 /**
21925  * @class Roo.bootstrap.Table.AbstractSelectionModel
21926  * @extends Roo.util.Observable
21927  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21928  * implemented by descendant classes.  This class should not be directly instantiated.
21929  * @constructor
21930  */
21931 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21932     this.locked = false;
21933     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21934 };
21935
21936
21937 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21938     /** @ignore Called by the grid automatically. Do not call directly. */
21939     init : function(grid){
21940         this.grid = grid;
21941         this.initEvents();
21942     },
21943
21944     /**
21945      * Locks the selections.
21946      */
21947     lock : function(){
21948         this.locked = true;
21949     },
21950
21951     /**
21952      * Unlocks the selections.
21953      */
21954     unlock : function(){
21955         this.locked = false;
21956     },
21957
21958     /**
21959      * Returns true if the selections are locked.
21960      * @return {Boolean}
21961      */
21962     isLocked : function(){
21963         return this.locked;
21964     }
21965 });
21966 /**
21967  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21968  * @class Roo.bootstrap.Table.RowSelectionModel
21969  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21970  * It supports multiple selections and keyboard selection/navigation. 
21971  * @constructor
21972  * @param {Object} config
21973  */
21974
21975 Roo.bootstrap.Table.RowSelectionModel = function(config){
21976     Roo.apply(this, config);
21977     this.selections = new Roo.util.MixedCollection(false, function(o){
21978         return o.id;
21979     });
21980
21981     this.last = false;
21982     this.lastActive = false;
21983
21984     this.addEvents({
21985         /**
21986              * @event selectionchange
21987              * Fires when the selection changes
21988              * @param {SelectionModel} this
21989              */
21990             "selectionchange" : true,
21991         /**
21992              * @event afterselectionchange
21993              * Fires after the selection changes (eg. by key press or clicking)
21994              * @param {SelectionModel} this
21995              */
21996             "afterselectionchange" : true,
21997         /**
21998              * @event beforerowselect
21999              * Fires when a row is selected being selected, return false to cancel.
22000              * @param {SelectionModel} this
22001              * @param {Number} rowIndex The selected index
22002              * @param {Boolean} keepExisting False if other selections will be cleared
22003              */
22004             "beforerowselect" : true,
22005         /**
22006              * @event rowselect
22007              * Fires when a row is selected.
22008              * @param {SelectionModel} this
22009              * @param {Number} rowIndex The selected index
22010              * @param {Roo.data.Record} r The record
22011              */
22012             "rowselect" : true,
22013         /**
22014              * @event rowdeselect
22015              * Fires when a row is deselected.
22016              * @param {SelectionModel} this
22017              * @param {Number} rowIndex The selected index
22018              */
22019         "rowdeselect" : true
22020     });
22021     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22022     this.locked = false;
22023 };
22024
22025 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22026     /**
22027      * @cfg {Boolean} singleSelect
22028      * True to allow selection of only one row at a time (defaults to false)
22029      */
22030     singleSelect : false,
22031
22032     // private
22033     initEvents : function(){
22034
22035         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22036             this.grid.on("mousedown", this.handleMouseDown, this);
22037         }else{ // allow click to work like normal
22038             this.grid.on("rowclick", this.handleDragableRowClick, this);
22039         }
22040
22041         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22042             "up" : function(e){
22043                 if(!e.shiftKey){
22044                     this.selectPrevious(e.shiftKey);
22045                 }else if(this.last !== false && this.lastActive !== false){
22046                     var last = this.last;
22047                     this.selectRange(this.last,  this.lastActive-1);
22048                     this.grid.getView().focusRow(this.lastActive);
22049                     if(last !== false){
22050                         this.last = last;
22051                     }
22052                 }else{
22053                     this.selectFirstRow();
22054                 }
22055                 this.fireEvent("afterselectionchange", this);
22056             },
22057             "down" : function(e){
22058                 if(!e.shiftKey){
22059                     this.selectNext(e.shiftKey);
22060                 }else if(this.last !== false && this.lastActive !== false){
22061                     var last = this.last;
22062                     this.selectRange(this.last,  this.lastActive+1);
22063                     this.grid.getView().focusRow(this.lastActive);
22064                     if(last !== false){
22065                         this.last = last;
22066                     }
22067                 }else{
22068                     this.selectFirstRow();
22069                 }
22070                 this.fireEvent("afterselectionchange", this);
22071             },
22072             scope: this
22073         });
22074
22075         var view = this.grid.view;
22076         view.on("refresh", this.onRefresh, this);
22077         view.on("rowupdated", this.onRowUpdated, this);
22078         view.on("rowremoved", this.onRemove, this);
22079     },
22080
22081     // private
22082     onRefresh : function(){
22083         var ds = this.grid.dataSource, i, v = this.grid.view;
22084         var s = this.selections;
22085         s.each(function(r){
22086             if((i = ds.indexOfId(r.id)) != -1){
22087                 v.onRowSelect(i);
22088             }else{
22089                 s.remove(r);
22090             }
22091         });
22092     },
22093
22094     // private
22095     onRemove : function(v, index, r){
22096         this.selections.remove(r);
22097     },
22098
22099     // private
22100     onRowUpdated : function(v, index, r){
22101         if(this.isSelected(r)){
22102             v.onRowSelect(index);
22103         }
22104     },
22105
22106     /**
22107      * Select records.
22108      * @param {Array} records The records to select
22109      * @param {Boolean} keepExisting (optional) True to keep existing selections
22110      */
22111     selectRecords : function(records, keepExisting){
22112         if(!keepExisting){
22113             this.clearSelections();
22114         }
22115         var ds = this.grid.dataSource;
22116         for(var i = 0, len = records.length; i < len; i++){
22117             this.selectRow(ds.indexOf(records[i]), true);
22118         }
22119     },
22120
22121     /**
22122      * Gets the number of selected rows.
22123      * @return {Number}
22124      */
22125     getCount : function(){
22126         return this.selections.length;
22127     },
22128
22129     /**
22130      * Selects the first row in the grid.
22131      */
22132     selectFirstRow : function(){
22133         this.selectRow(0);
22134     },
22135
22136     /**
22137      * Select the last row.
22138      * @param {Boolean} keepExisting (optional) True to keep existing selections
22139      */
22140     selectLastRow : function(keepExisting){
22141         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22142     },
22143
22144     /**
22145      * Selects the row immediately following the last selected row.
22146      * @param {Boolean} keepExisting (optional) True to keep existing selections
22147      */
22148     selectNext : function(keepExisting){
22149         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22150             this.selectRow(this.last+1, keepExisting);
22151             this.grid.getView().focusRow(this.last);
22152         }
22153     },
22154
22155     /**
22156      * Selects the row that precedes the last selected row.
22157      * @param {Boolean} keepExisting (optional) True to keep existing selections
22158      */
22159     selectPrevious : function(keepExisting){
22160         if(this.last){
22161             this.selectRow(this.last-1, keepExisting);
22162             this.grid.getView().focusRow(this.last);
22163         }
22164     },
22165
22166     /**
22167      * Returns the selected records
22168      * @return {Array} Array of selected records
22169      */
22170     getSelections : function(){
22171         return [].concat(this.selections.items);
22172     },
22173
22174     /**
22175      * Returns the first selected record.
22176      * @return {Record}
22177      */
22178     getSelected : function(){
22179         return this.selections.itemAt(0);
22180     },
22181
22182
22183     /**
22184      * Clears all selections.
22185      */
22186     clearSelections : function(fast){
22187         if(this.locked) {
22188             return;
22189         }
22190         if(fast !== true){
22191             var ds = this.grid.dataSource;
22192             var s = this.selections;
22193             s.each(function(r){
22194                 this.deselectRow(ds.indexOfId(r.id));
22195             }, this);
22196             s.clear();
22197         }else{
22198             this.selections.clear();
22199         }
22200         this.last = false;
22201     },
22202
22203
22204     /**
22205      * Selects all rows.
22206      */
22207     selectAll : function(){
22208         if(this.locked) {
22209             return;
22210         }
22211         this.selections.clear();
22212         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22213             this.selectRow(i, true);
22214         }
22215     },
22216
22217     /**
22218      * Returns True if there is a selection.
22219      * @return {Boolean}
22220      */
22221     hasSelection : function(){
22222         return this.selections.length > 0;
22223     },
22224
22225     /**
22226      * Returns True if the specified row is selected.
22227      * @param {Number/Record} record The record or index of the record to check
22228      * @return {Boolean}
22229      */
22230     isSelected : function(index){
22231         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22232         return (r && this.selections.key(r.id) ? true : false);
22233     },
22234
22235     /**
22236      * Returns True if the specified record id is selected.
22237      * @param {String} id The id of record to check
22238      * @return {Boolean}
22239      */
22240     isIdSelected : function(id){
22241         return (this.selections.key(id) ? true : false);
22242     },
22243
22244     // private
22245     handleMouseDown : function(e, t){
22246         var view = this.grid.getView(), rowIndex;
22247         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22248             return;
22249         };
22250         if(e.shiftKey && this.last !== false){
22251             var last = this.last;
22252             this.selectRange(last, rowIndex, e.ctrlKey);
22253             this.last = last; // reset the last
22254             view.focusRow(rowIndex);
22255         }else{
22256             var isSelected = this.isSelected(rowIndex);
22257             if(e.button !== 0 && isSelected){
22258                 view.focusRow(rowIndex);
22259             }else if(e.ctrlKey && isSelected){
22260                 this.deselectRow(rowIndex);
22261             }else if(!isSelected){
22262                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22263                 view.focusRow(rowIndex);
22264             }
22265         }
22266         this.fireEvent("afterselectionchange", this);
22267     },
22268     // private
22269     handleDragableRowClick :  function(grid, rowIndex, e) 
22270     {
22271         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22272             this.selectRow(rowIndex, false);
22273             grid.view.focusRow(rowIndex);
22274              this.fireEvent("afterselectionchange", this);
22275         }
22276     },
22277     
22278     /**
22279      * Selects multiple rows.
22280      * @param {Array} rows Array of the indexes of the row to select
22281      * @param {Boolean} keepExisting (optional) True to keep existing selections
22282      */
22283     selectRows : function(rows, keepExisting){
22284         if(!keepExisting){
22285             this.clearSelections();
22286         }
22287         for(var i = 0, len = rows.length; i < len; i++){
22288             this.selectRow(rows[i], true);
22289         }
22290     },
22291
22292     /**
22293      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22294      * @param {Number} startRow The index of the first row in the range
22295      * @param {Number} endRow The index of the last row in the range
22296      * @param {Boolean} keepExisting (optional) True to retain existing selections
22297      */
22298     selectRange : function(startRow, endRow, keepExisting){
22299         if(this.locked) {
22300             return;
22301         }
22302         if(!keepExisting){
22303             this.clearSelections();
22304         }
22305         if(startRow <= endRow){
22306             for(var i = startRow; i <= endRow; i++){
22307                 this.selectRow(i, true);
22308             }
22309         }else{
22310             for(var i = startRow; i >= endRow; i--){
22311                 this.selectRow(i, true);
22312             }
22313         }
22314     },
22315
22316     /**
22317      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22318      * @param {Number} startRow The index of the first row in the range
22319      * @param {Number} endRow The index of the last row in the range
22320      */
22321     deselectRange : function(startRow, endRow, preventViewNotify){
22322         if(this.locked) {
22323             return;
22324         }
22325         for(var i = startRow; i <= endRow; i++){
22326             this.deselectRow(i, preventViewNotify);
22327         }
22328     },
22329
22330     /**
22331      * Selects a row.
22332      * @param {Number} row The index of the row to select
22333      * @param {Boolean} keepExisting (optional) True to keep existing selections
22334      */
22335     selectRow : function(index, keepExisting, preventViewNotify){
22336         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22337             return;
22338         }
22339         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22340             if(!keepExisting || this.singleSelect){
22341                 this.clearSelections();
22342             }
22343             var r = this.grid.dataSource.getAt(index);
22344             this.selections.add(r);
22345             this.last = this.lastActive = index;
22346             if(!preventViewNotify){
22347                 this.grid.getView().onRowSelect(index);
22348             }
22349             this.fireEvent("rowselect", this, index, r);
22350             this.fireEvent("selectionchange", this);
22351         }
22352     },
22353
22354     /**
22355      * Deselects a row.
22356      * @param {Number} row The index of the row to deselect
22357      */
22358     deselectRow : function(index, preventViewNotify){
22359         if(this.locked) {
22360             return;
22361         }
22362         if(this.last == index){
22363             this.last = false;
22364         }
22365         if(this.lastActive == index){
22366             this.lastActive = false;
22367         }
22368         var r = this.grid.dataSource.getAt(index);
22369         this.selections.remove(r);
22370         if(!preventViewNotify){
22371             this.grid.getView().onRowDeselect(index);
22372         }
22373         this.fireEvent("rowdeselect", this, index);
22374         this.fireEvent("selectionchange", this);
22375     },
22376
22377     // private
22378     restoreLast : function(){
22379         if(this._last){
22380             this.last = this._last;
22381         }
22382     },
22383
22384     // private
22385     acceptsNav : function(row, col, cm){
22386         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22387     },
22388
22389     // private
22390     onEditorKey : function(field, e){
22391         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22392         if(k == e.TAB){
22393             e.stopEvent();
22394             ed.completeEdit();
22395             if(e.shiftKey){
22396                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22397             }else{
22398                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22399             }
22400         }else if(k == e.ENTER && !e.ctrlKey){
22401             e.stopEvent();
22402             ed.completeEdit();
22403             if(e.shiftKey){
22404                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22405             }else{
22406                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22407             }
22408         }else if(k == e.ESC){
22409             ed.cancelEdit();
22410         }
22411         if(newCell){
22412             g.startEditing(newCell[0], newCell[1]);
22413         }
22414     }
22415 });/*
22416  * Based on:
22417  * Ext JS Library 1.1.1
22418  * Copyright(c) 2006-2007, Ext JS, LLC.
22419  *
22420  * Originally Released Under LGPL - original licence link has changed is not relivant.
22421  *
22422  * Fork - LGPL
22423  * <script type="text/javascript">
22424  */
22425  
22426 /**
22427  * @class Roo.bootstrap.PagingToolbar
22428  * @extends Roo.bootstrap.NavSimplebar
22429  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22430  * @constructor
22431  * Create a new PagingToolbar
22432  * @param {Object} config The config object
22433  * @param {Roo.data.Store} store
22434  */
22435 Roo.bootstrap.PagingToolbar = function(config)
22436 {
22437     // old args format still supported... - xtype is prefered..
22438         // created from xtype...
22439     
22440     this.ds = config.dataSource;
22441     
22442     if (config.store && !this.ds) {
22443         this.store= Roo.factory(config.store, Roo.data);
22444         this.ds = this.store;
22445         this.ds.xmodule = this.xmodule || false;
22446     }
22447     
22448     this.toolbarItems = [];
22449     if (config.items) {
22450         this.toolbarItems = config.items;
22451     }
22452     
22453     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22454     
22455     this.cursor = 0;
22456     
22457     if (this.ds) { 
22458         this.bind(this.ds);
22459     }
22460     
22461     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22462     
22463 };
22464
22465 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22466     /**
22467      * @cfg {Roo.data.Store} dataSource
22468      * The underlying data store providing the paged data
22469      */
22470     /**
22471      * @cfg {String/HTMLElement/Element} container
22472      * container The id or element that will contain the toolbar
22473      */
22474     /**
22475      * @cfg {Boolean} displayInfo
22476      * True to display the displayMsg (defaults to false)
22477      */
22478     /**
22479      * @cfg {Number} pageSize
22480      * The number of records to display per page (defaults to 20)
22481      */
22482     pageSize: 20,
22483     /**
22484      * @cfg {String} displayMsg
22485      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22486      */
22487     displayMsg : 'Displaying {0} - {1} of {2}',
22488     /**
22489      * @cfg {String} emptyMsg
22490      * The message to display when no records are found (defaults to "No data to display")
22491      */
22492     emptyMsg : 'No data to display',
22493     /**
22494      * Customizable piece of the default paging text (defaults to "Page")
22495      * @type String
22496      */
22497     beforePageText : "Page",
22498     /**
22499      * Customizable piece of the default paging text (defaults to "of %0")
22500      * @type String
22501      */
22502     afterPageText : "of {0}",
22503     /**
22504      * Customizable piece of the default paging text (defaults to "First Page")
22505      * @type String
22506      */
22507     firstText : "First Page",
22508     /**
22509      * Customizable piece of the default paging text (defaults to "Previous Page")
22510      * @type String
22511      */
22512     prevText : "Previous Page",
22513     /**
22514      * Customizable piece of the default paging text (defaults to "Next Page")
22515      * @type String
22516      */
22517     nextText : "Next Page",
22518     /**
22519      * Customizable piece of the default paging text (defaults to "Last Page")
22520      * @type String
22521      */
22522     lastText : "Last Page",
22523     /**
22524      * Customizable piece of the default paging text (defaults to "Refresh")
22525      * @type String
22526      */
22527     refreshText : "Refresh",
22528
22529     buttons : false,
22530     // private
22531     onRender : function(ct, position) 
22532     {
22533         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22534         this.navgroup.parentId = this.id;
22535         this.navgroup.onRender(this.el, null);
22536         // add the buttons to the navgroup
22537         
22538         if(this.displayInfo){
22539             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22540             this.displayEl = this.el.select('.x-paging-info', true).first();
22541 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22542 //            this.displayEl = navel.el.select('span',true).first();
22543         }
22544         
22545         var _this = this;
22546         
22547         if(this.buttons){
22548             Roo.each(_this.buttons, function(e){ // this might need to use render????
22549                Roo.factory(e).onRender(_this.el, null);
22550             });
22551         }
22552             
22553         Roo.each(_this.toolbarItems, function(e) {
22554             _this.navgroup.addItem(e);
22555         });
22556         
22557         
22558         this.first = this.navgroup.addItem({
22559             tooltip: this.firstText,
22560             cls: "prev",
22561             icon : 'fa fa-backward',
22562             disabled: true,
22563             preventDefault: true,
22564             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22565         });
22566         
22567         this.prev =  this.navgroup.addItem({
22568             tooltip: this.prevText,
22569             cls: "prev",
22570             icon : 'fa fa-step-backward',
22571             disabled: true,
22572             preventDefault: true,
22573             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22574         });
22575     //this.addSeparator();
22576         
22577         
22578         var field = this.navgroup.addItem( {
22579             tagtype : 'span',
22580             cls : 'x-paging-position',
22581             
22582             html : this.beforePageText  +
22583                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22584                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22585          } ); //?? escaped?
22586         
22587         this.field = field.el.select('input', true).first();
22588         this.field.on("keydown", this.onPagingKeydown, this);
22589         this.field.on("focus", function(){this.dom.select();});
22590     
22591     
22592         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22593         //this.field.setHeight(18);
22594         //this.addSeparator();
22595         this.next = this.navgroup.addItem({
22596             tooltip: this.nextText,
22597             cls: "next",
22598             html : ' <i class="fa fa-step-forward">',
22599             disabled: true,
22600             preventDefault: true,
22601             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22602         });
22603         this.last = this.navgroup.addItem({
22604             tooltip: this.lastText,
22605             icon : 'fa fa-forward',
22606             cls: "next",
22607             disabled: true,
22608             preventDefault: true,
22609             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22610         });
22611     //this.addSeparator();
22612         this.loading = this.navgroup.addItem({
22613             tooltip: this.refreshText,
22614             icon: 'fa fa-refresh',
22615             preventDefault: true,
22616             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22617         });
22618         
22619     },
22620
22621     // private
22622     updateInfo : function(){
22623         if(this.displayEl){
22624             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22625             var msg = count == 0 ?
22626                 this.emptyMsg :
22627                 String.format(
22628                     this.displayMsg,
22629                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22630                 );
22631             this.displayEl.update(msg);
22632         }
22633     },
22634
22635     // private
22636     onLoad : function(ds, r, o){
22637        this.cursor = o.params ? o.params.start : 0;
22638        var d = this.getPageData(),
22639             ap = d.activePage,
22640             ps = d.pages;
22641         
22642        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22643        this.field.dom.value = ap;
22644        this.first.setDisabled(ap == 1);
22645        this.prev.setDisabled(ap == 1);
22646        this.next.setDisabled(ap == ps);
22647        this.last.setDisabled(ap == ps);
22648        this.loading.enable();
22649        this.updateInfo();
22650     },
22651
22652     // private
22653     getPageData : function(){
22654         var total = this.ds.getTotalCount();
22655         return {
22656             total : total,
22657             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22658             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22659         };
22660     },
22661
22662     // private
22663     onLoadError : function(){
22664         this.loading.enable();
22665     },
22666
22667     // private
22668     onPagingKeydown : function(e){
22669         var k = e.getKey();
22670         var d = this.getPageData();
22671         if(k == e.RETURN){
22672             var v = this.field.dom.value, pageNum;
22673             if(!v || isNaN(pageNum = parseInt(v, 10))){
22674                 this.field.dom.value = d.activePage;
22675                 return;
22676             }
22677             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22678             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22679             e.stopEvent();
22680         }
22681         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))
22682         {
22683           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22684           this.field.dom.value = pageNum;
22685           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22686           e.stopEvent();
22687         }
22688         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22689         {
22690           var v = this.field.dom.value, pageNum; 
22691           var increment = (e.shiftKey) ? 10 : 1;
22692           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22693                 increment *= -1;
22694           }
22695           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22696             this.field.dom.value = d.activePage;
22697             return;
22698           }
22699           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22700           {
22701             this.field.dom.value = parseInt(v, 10) + increment;
22702             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22703             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22704           }
22705           e.stopEvent();
22706         }
22707     },
22708
22709     // private
22710     beforeLoad : function(){
22711         if(this.loading){
22712             this.loading.disable();
22713         }
22714     },
22715
22716     // private
22717     onClick : function(which){
22718         
22719         var ds = this.ds;
22720         if (!ds) {
22721             return;
22722         }
22723         
22724         switch(which){
22725             case "first":
22726                 ds.load({params:{start: 0, limit: this.pageSize}});
22727             break;
22728             case "prev":
22729                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22730             break;
22731             case "next":
22732                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22733             break;
22734             case "last":
22735                 var total = ds.getTotalCount();
22736                 var extra = total % this.pageSize;
22737                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22738                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22739             break;
22740             case "refresh":
22741                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22742             break;
22743         }
22744     },
22745
22746     /**
22747      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22748      * @param {Roo.data.Store} store The data store to unbind
22749      */
22750     unbind : function(ds){
22751         ds.un("beforeload", this.beforeLoad, this);
22752         ds.un("load", this.onLoad, this);
22753         ds.un("loadexception", this.onLoadError, this);
22754         ds.un("remove", this.updateInfo, this);
22755         ds.un("add", this.updateInfo, this);
22756         this.ds = undefined;
22757     },
22758
22759     /**
22760      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22761      * @param {Roo.data.Store} store The data store to bind
22762      */
22763     bind : function(ds){
22764         ds.on("beforeload", this.beforeLoad, this);
22765         ds.on("load", this.onLoad, this);
22766         ds.on("loadexception", this.onLoadError, this);
22767         ds.on("remove", this.updateInfo, this);
22768         ds.on("add", this.updateInfo, this);
22769         this.ds = ds;
22770     }
22771 });/*
22772  * - LGPL
22773  *
22774  * element
22775  * 
22776  */
22777
22778 /**
22779  * @class Roo.bootstrap.MessageBar
22780  * @extends Roo.bootstrap.Component
22781  * Bootstrap MessageBar class
22782  * @cfg {String} html contents of the MessageBar
22783  * @cfg {String} weight (info | success | warning | danger) default info
22784  * @cfg {String} beforeClass insert the bar before the given class
22785  * @cfg {Boolean} closable (true | false) default false
22786  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22787  * 
22788  * @constructor
22789  * Create a new Element
22790  * @param {Object} config The config object
22791  */
22792
22793 Roo.bootstrap.MessageBar = function(config){
22794     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22795 };
22796
22797 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22798     
22799     html: '',
22800     weight: 'info',
22801     closable: false,
22802     fixed: false,
22803     beforeClass: 'bootstrap-sticky-wrap',
22804     
22805     getAutoCreate : function(){
22806         
22807         var cfg = {
22808             tag: 'div',
22809             cls: 'alert alert-dismissable alert-' + this.weight,
22810             cn: [
22811                 {
22812                     tag: 'span',
22813                     cls: 'message',
22814                     html: this.html || ''
22815                 }
22816             ]
22817         };
22818         
22819         if(this.fixed){
22820             cfg.cls += ' alert-messages-fixed';
22821         }
22822         
22823         if(this.closable){
22824             cfg.cn.push({
22825                 tag: 'button',
22826                 cls: 'close',
22827                 html: 'x'
22828             });
22829         }
22830         
22831         return cfg;
22832     },
22833     
22834     onRender : function(ct, position)
22835     {
22836         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22837         
22838         if(!this.el){
22839             var cfg = Roo.apply({},  this.getAutoCreate());
22840             cfg.id = Roo.id();
22841             
22842             if (this.cls) {
22843                 cfg.cls += ' ' + this.cls;
22844             }
22845             if (this.style) {
22846                 cfg.style = this.style;
22847             }
22848             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22849             
22850             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22851         }
22852         
22853         this.el.select('>button.close').on('click', this.hide, this);
22854         
22855     },
22856     
22857     show : function()
22858     {
22859         if (!this.rendered) {
22860             this.render();
22861         }
22862         
22863         this.el.show();
22864         
22865         this.fireEvent('show', this);
22866         
22867     },
22868     
22869     hide : function()
22870     {
22871         if (!this.rendered) {
22872             this.render();
22873         }
22874         
22875         this.el.hide();
22876         
22877         this.fireEvent('hide', this);
22878     },
22879     
22880     update : function()
22881     {
22882 //        var e = this.el.dom.firstChild;
22883 //        
22884 //        if(this.closable){
22885 //            e = e.nextSibling;
22886 //        }
22887 //        
22888 //        e.data = this.html || '';
22889
22890         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22891     }
22892    
22893 });
22894
22895  
22896
22897      /*
22898  * - LGPL
22899  *
22900  * Graph
22901  * 
22902  */
22903
22904
22905 /**
22906  * @class Roo.bootstrap.Graph
22907  * @extends Roo.bootstrap.Component
22908  * Bootstrap Graph class
22909 > Prameters
22910  -sm {number} sm 4
22911  -md {number} md 5
22912  @cfg {String} graphtype  bar | vbar | pie
22913  @cfg {number} g_x coodinator | centre x (pie)
22914  @cfg {number} g_y coodinator | centre y (pie)
22915  @cfg {number} g_r radius (pie)
22916  @cfg {number} g_height height of the chart (respected by all elements in the set)
22917  @cfg {number} g_width width of the chart (respected by all elements in the set)
22918  @cfg {Object} title The title of the chart
22919     
22920  -{Array}  values
22921  -opts (object) options for the chart 
22922      o {
22923      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22924      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22925      o vgutter (number)
22926      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.
22927      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22928      o to
22929      o stretch (boolean)
22930      o }
22931  -opts (object) options for the pie
22932      o{
22933      o cut
22934      o startAngle (number)
22935      o endAngle (number)
22936      } 
22937  *
22938  * @constructor
22939  * Create a new Input
22940  * @param {Object} config The config object
22941  */
22942
22943 Roo.bootstrap.Graph = function(config){
22944     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22945     
22946     this.addEvents({
22947         // img events
22948         /**
22949          * @event click
22950          * The img click event for the img.
22951          * @param {Roo.EventObject} e
22952          */
22953         "click" : true
22954     });
22955 };
22956
22957 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22958     
22959     sm: 4,
22960     md: 5,
22961     graphtype: 'bar',
22962     g_height: 250,
22963     g_width: 400,
22964     g_x: 50,
22965     g_y: 50,
22966     g_r: 30,
22967     opts:{
22968         //g_colors: this.colors,
22969         g_type: 'soft',
22970         g_gutter: '20%'
22971
22972     },
22973     title : false,
22974
22975     getAutoCreate : function(){
22976         
22977         var cfg = {
22978             tag: 'div',
22979             html : null
22980         };
22981         
22982         
22983         return  cfg;
22984     },
22985
22986     onRender : function(ct,position){
22987         
22988         
22989         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22990         
22991         if (typeof(Raphael) == 'undefined') {
22992             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
22993             return;
22994         }
22995         
22996         this.raphael = Raphael(this.el.dom);
22997         
22998                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22999                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23000                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23001                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23002                 /*
23003                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23004                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23005                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23006                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23007                 
23008                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23009                 r.barchart(330, 10, 300, 220, data1);
23010                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23011                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23012                 */
23013                 
23014                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23015                 // r.barchart(30, 30, 560, 250,  xdata, {
23016                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23017                 //     axis : "0 0 1 1",
23018                 //     axisxlabels :  xdata
23019                 //     //yvalues : cols,
23020                    
23021                 // });
23022 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23023 //        
23024 //        this.load(null,xdata,{
23025 //                axis : "0 0 1 1",
23026 //                axisxlabels :  xdata
23027 //                });
23028
23029     },
23030
23031     load : function(graphtype,xdata,opts)
23032     {
23033         this.raphael.clear();
23034         if(!graphtype) {
23035             graphtype = this.graphtype;
23036         }
23037         if(!opts){
23038             opts = this.opts;
23039         }
23040         var r = this.raphael,
23041             fin = function () {
23042                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23043             },
23044             fout = function () {
23045                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23046             },
23047             pfin = function() {
23048                 this.sector.stop();
23049                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23050
23051                 if (this.label) {
23052                     this.label[0].stop();
23053                     this.label[0].attr({ r: 7.5 });
23054                     this.label[1].attr({ "font-weight": 800 });
23055                 }
23056             },
23057             pfout = function() {
23058                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23059
23060                 if (this.label) {
23061                     this.label[0].animate({ r: 5 }, 500, "bounce");
23062                     this.label[1].attr({ "font-weight": 400 });
23063                 }
23064             };
23065
23066         switch(graphtype){
23067             case 'bar':
23068                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23069                 break;
23070             case 'hbar':
23071                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23072                 break;
23073             case 'pie':
23074 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23075 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23076 //            
23077                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23078                 
23079                 break;
23080
23081         }
23082         
23083         if(this.title){
23084             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23085         }
23086         
23087     },
23088     
23089     setTitle: function(o)
23090     {
23091         this.title = o;
23092     },
23093     
23094     initEvents: function() {
23095         
23096         if(!this.href){
23097             this.el.on('click', this.onClick, this);
23098         }
23099     },
23100     
23101     onClick : function(e)
23102     {
23103         Roo.log('img onclick');
23104         this.fireEvent('click', this, e);
23105     }
23106    
23107 });
23108
23109  
23110 /*
23111  * - LGPL
23112  *
23113  * numberBox
23114  * 
23115  */
23116 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23117
23118 /**
23119  * @class Roo.bootstrap.dash.NumberBox
23120  * @extends Roo.bootstrap.Component
23121  * Bootstrap NumberBox class
23122  * @cfg {String} headline Box headline
23123  * @cfg {String} content Box content
23124  * @cfg {String} icon Box icon
23125  * @cfg {String} footer Footer text
23126  * @cfg {String} fhref Footer href
23127  * 
23128  * @constructor
23129  * Create a new NumberBox
23130  * @param {Object} config The config object
23131  */
23132
23133
23134 Roo.bootstrap.dash.NumberBox = function(config){
23135     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23136     
23137 };
23138
23139 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23140     
23141     headline : '',
23142     content : '',
23143     icon : '',
23144     footer : '',
23145     fhref : '',
23146     ficon : '',
23147     
23148     getAutoCreate : function(){
23149         
23150         var cfg = {
23151             tag : 'div',
23152             cls : 'small-box ',
23153             cn : [
23154                 {
23155                     tag : 'div',
23156                     cls : 'inner',
23157                     cn :[
23158                         {
23159                             tag : 'h3',
23160                             cls : 'roo-headline',
23161                             html : this.headline
23162                         },
23163                         {
23164                             tag : 'p',
23165                             cls : 'roo-content',
23166                             html : this.content
23167                         }
23168                     ]
23169                 }
23170             ]
23171         };
23172         
23173         if(this.icon){
23174             cfg.cn.push({
23175                 tag : 'div',
23176                 cls : 'icon',
23177                 cn :[
23178                     {
23179                         tag : 'i',
23180                         cls : 'ion ' + this.icon
23181                     }
23182                 ]
23183             });
23184         }
23185         
23186         if(this.footer){
23187             var footer = {
23188                 tag : 'a',
23189                 cls : 'small-box-footer',
23190                 href : this.fhref || '#',
23191                 html : this.footer
23192             };
23193             
23194             cfg.cn.push(footer);
23195             
23196         }
23197         
23198         return  cfg;
23199     },
23200
23201     onRender : function(ct,position){
23202         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23203
23204
23205        
23206                 
23207     },
23208
23209     setHeadline: function (value)
23210     {
23211         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23212     },
23213     
23214     setFooter: function (value, href)
23215     {
23216         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23217         
23218         if(href){
23219             this.el.select('a.small-box-footer',true).first().attr('href', href);
23220         }
23221         
23222     },
23223
23224     setContent: function (value)
23225     {
23226         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23227     },
23228
23229     initEvents: function() 
23230     {   
23231         
23232     }
23233     
23234 });
23235
23236  
23237 /*
23238  * - LGPL
23239  *
23240  * TabBox
23241  * 
23242  */
23243 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23244
23245 /**
23246  * @class Roo.bootstrap.dash.TabBox
23247  * @extends Roo.bootstrap.Component
23248  * Bootstrap TabBox class
23249  * @cfg {String} title Title of the TabBox
23250  * @cfg {String} icon Icon of the TabBox
23251  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23252  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23253  * 
23254  * @constructor
23255  * Create a new TabBox
23256  * @param {Object} config The config object
23257  */
23258
23259
23260 Roo.bootstrap.dash.TabBox = function(config){
23261     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23262     this.addEvents({
23263         // raw events
23264         /**
23265          * @event addpane
23266          * When a pane is added
23267          * @param {Roo.bootstrap.dash.TabPane} pane
23268          */
23269         "addpane" : true,
23270         /**
23271          * @event activatepane
23272          * When a pane is activated
23273          * @param {Roo.bootstrap.dash.TabPane} pane
23274          */
23275         "activatepane" : true
23276         
23277          
23278     });
23279     
23280     this.panes = [];
23281 };
23282
23283 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23284
23285     title : '',
23286     icon : false,
23287     showtabs : true,
23288     tabScrollable : false,
23289     
23290     getChildContainer : function()
23291     {
23292         return this.el.select('.tab-content', true).first();
23293     },
23294     
23295     getAutoCreate : function(){
23296         
23297         var header = {
23298             tag: 'li',
23299             cls: 'pull-left header',
23300             html: this.title,
23301             cn : []
23302         };
23303         
23304         if(this.icon){
23305             header.cn.push({
23306                 tag: 'i',
23307                 cls: 'fa ' + this.icon
23308             });
23309         }
23310         
23311         var h = {
23312             tag: 'ul',
23313             cls: 'nav nav-tabs pull-right',
23314             cn: [
23315                 header
23316             ]
23317         };
23318         
23319         if(this.tabScrollable){
23320             h = {
23321                 tag: 'div',
23322                 cls: 'tab-header',
23323                 cn: [
23324                     {
23325                         tag: 'ul',
23326                         cls: 'nav nav-tabs pull-right',
23327                         cn: [
23328                             header
23329                         ]
23330                     }
23331                 ]
23332             };
23333         }
23334         
23335         var cfg = {
23336             tag: 'div',
23337             cls: 'nav-tabs-custom',
23338             cn: [
23339                 h,
23340                 {
23341                     tag: 'div',
23342                     cls: 'tab-content no-padding',
23343                     cn: []
23344                 }
23345             ]
23346         };
23347
23348         return  cfg;
23349     },
23350     initEvents : function()
23351     {
23352         //Roo.log('add add pane handler');
23353         this.on('addpane', this.onAddPane, this);
23354     },
23355      /**
23356      * Updates the box title
23357      * @param {String} html to set the title to.
23358      */
23359     setTitle : function(value)
23360     {
23361         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23362     },
23363     onAddPane : function(pane)
23364     {
23365         this.panes.push(pane);
23366         //Roo.log('addpane');
23367         //Roo.log(pane);
23368         // tabs are rendere left to right..
23369         if(!this.showtabs){
23370             return;
23371         }
23372         
23373         var ctr = this.el.select('.nav-tabs', true).first();
23374          
23375          
23376         var existing = ctr.select('.nav-tab',true);
23377         var qty = existing.getCount();;
23378         
23379         
23380         var tab = ctr.createChild({
23381             tag : 'li',
23382             cls : 'nav-tab' + (qty ? '' : ' active'),
23383             cn : [
23384                 {
23385                     tag : 'a',
23386                     href:'#',
23387                     html : pane.title
23388                 }
23389             ]
23390         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23391         pane.tab = tab;
23392         
23393         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23394         if (!qty) {
23395             pane.el.addClass('active');
23396         }
23397         
23398                 
23399     },
23400     onTabClick : function(ev,un,ob,pane)
23401     {
23402         //Roo.log('tab - prev default');
23403         ev.preventDefault();
23404         
23405         
23406         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23407         pane.tab.addClass('active');
23408         //Roo.log(pane.title);
23409         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23410         // technically we should have a deactivate event.. but maybe add later.
23411         // and it should not de-activate the selected tab...
23412         this.fireEvent('activatepane', pane);
23413         pane.el.addClass('active');
23414         pane.fireEvent('activate');
23415         
23416         
23417     },
23418     
23419     getActivePane : function()
23420     {
23421         var r = false;
23422         Roo.each(this.panes, function(p) {
23423             if(p.el.hasClass('active')){
23424                 r = p;
23425                 return false;
23426             }
23427             
23428             return;
23429         });
23430         
23431         return r;
23432     }
23433     
23434     
23435 });
23436
23437  
23438 /*
23439  * - LGPL
23440  *
23441  * Tab pane
23442  * 
23443  */
23444 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23445 /**
23446  * @class Roo.bootstrap.TabPane
23447  * @extends Roo.bootstrap.Component
23448  * Bootstrap TabPane class
23449  * @cfg {Boolean} active (false | true) Default false
23450  * @cfg {String} title title of panel
23451
23452  * 
23453  * @constructor
23454  * Create a new TabPane
23455  * @param {Object} config The config object
23456  */
23457
23458 Roo.bootstrap.dash.TabPane = function(config){
23459     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23460     
23461     this.addEvents({
23462         // raw events
23463         /**
23464          * @event activate
23465          * When a pane is activated
23466          * @param {Roo.bootstrap.dash.TabPane} pane
23467          */
23468         "activate" : true
23469          
23470     });
23471 };
23472
23473 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23474     
23475     active : false,
23476     title : '',
23477     
23478     // the tabBox that this is attached to.
23479     tab : false,
23480      
23481     getAutoCreate : function() 
23482     {
23483         var cfg = {
23484             tag: 'div',
23485             cls: 'tab-pane'
23486         };
23487         
23488         if(this.active){
23489             cfg.cls += ' active';
23490         }
23491         
23492         return cfg;
23493     },
23494     initEvents  : function()
23495     {
23496         //Roo.log('trigger add pane handler');
23497         this.parent().fireEvent('addpane', this)
23498     },
23499     
23500      /**
23501      * Updates the tab title 
23502      * @param {String} html to set the title to.
23503      */
23504     setTitle: function(str)
23505     {
23506         if (!this.tab) {
23507             return;
23508         }
23509         this.title = str;
23510         this.tab.select('a', true).first().dom.innerHTML = str;
23511         
23512     }
23513     
23514     
23515     
23516 });
23517
23518  
23519
23520
23521  /*
23522  * - LGPL
23523  *
23524  * menu
23525  * 
23526  */
23527 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23528
23529 /**
23530  * @class Roo.bootstrap.menu.Menu
23531  * @extends Roo.bootstrap.Component
23532  * Bootstrap Menu class - container for Menu
23533  * @cfg {String} html Text of the menu
23534  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23535  * @cfg {String} icon Font awesome icon
23536  * @cfg {String} pos Menu align to (top | bottom) default bottom
23537  * 
23538  * 
23539  * @constructor
23540  * Create a new Menu
23541  * @param {Object} config The config object
23542  */
23543
23544
23545 Roo.bootstrap.menu.Menu = function(config){
23546     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23547     
23548     this.addEvents({
23549         /**
23550          * @event beforeshow
23551          * Fires before this menu is displayed
23552          * @param {Roo.bootstrap.menu.Menu} this
23553          */
23554         beforeshow : true,
23555         /**
23556          * @event beforehide
23557          * Fires before this menu is hidden
23558          * @param {Roo.bootstrap.menu.Menu} this
23559          */
23560         beforehide : true,
23561         /**
23562          * @event show
23563          * Fires after this menu is displayed
23564          * @param {Roo.bootstrap.menu.Menu} this
23565          */
23566         show : true,
23567         /**
23568          * @event hide
23569          * Fires after this menu is hidden
23570          * @param {Roo.bootstrap.menu.Menu} this
23571          */
23572         hide : true,
23573         /**
23574          * @event click
23575          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23576          * @param {Roo.bootstrap.menu.Menu} this
23577          * @param {Roo.EventObject} e
23578          */
23579         click : true
23580     });
23581     
23582 };
23583
23584 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23585     
23586     submenu : false,
23587     html : '',
23588     weight : 'default',
23589     icon : false,
23590     pos : 'bottom',
23591     
23592     
23593     getChildContainer : function() {
23594         if(this.isSubMenu){
23595             return this.el;
23596         }
23597         
23598         return this.el.select('ul.dropdown-menu', true).first();  
23599     },
23600     
23601     getAutoCreate : function()
23602     {
23603         var text = [
23604             {
23605                 tag : 'span',
23606                 cls : 'roo-menu-text',
23607                 html : this.html
23608             }
23609         ];
23610         
23611         if(this.icon){
23612             text.unshift({
23613                 tag : 'i',
23614                 cls : 'fa ' + this.icon
23615             })
23616         }
23617         
23618         
23619         var cfg = {
23620             tag : 'div',
23621             cls : 'btn-group',
23622             cn : [
23623                 {
23624                     tag : 'button',
23625                     cls : 'dropdown-button btn btn-' + this.weight,
23626                     cn : text
23627                 },
23628                 {
23629                     tag : 'button',
23630                     cls : 'dropdown-toggle btn btn-' + this.weight,
23631                     cn : [
23632                         {
23633                             tag : 'span',
23634                             cls : 'caret'
23635                         }
23636                     ]
23637                 },
23638                 {
23639                     tag : 'ul',
23640                     cls : 'dropdown-menu'
23641                 }
23642             ]
23643             
23644         };
23645         
23646         if(this.pos == 'top'){
23647             cfg.cls += ' dropup';
23648         }
23649         
23650         if(this.isSubMenu){
23651             cfg = {
23652                 tag : 'ul',
23653                 cls : 'dropdown-menu'
23654             }
23655         }
23656         
23657         return cfg;
23658     },
23659     
23660     onRender : function(ct, position)
23661     {
23662         this.isSubMenu = ct.hasClass('dropdown-submenu');
23663         
23664         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23665     },
23666     
23667     initEvents : function() 
23668     {
23669         if(this.isSubMenu){
23670             return;
23671         }
23672         
23673         this.hidden = true;
23674         
23675         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23676         this.triggerEl.on('click', this.onTriggerPress, this);
23677         
23678         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23679         this.buttonEl.on('click', this.onClick, this);
23680         
23681     },
23682     
23683     list : function()
23684     {
23685         if(this.isSubMenu){
23686             return this.el;
23687         }
23688         
23689         return this.el.select('ul.dropdown-menu', true).first();
23690     },
23691     
23692     onClick : function(e)
23693     {
23694         this.fireEvent("click", this, e);
23695     },
23696     
23697     onTriggerPress  : function(e)
23698     {   
23699         if (this.isVisible()) {
23700             this.hide();
23701         } else {
23702             this.show();
23703         }
23704     },
23705     
23706     isVisible : function(){
23707         return !this.hidden;
23708     },
23709     
23710     show : function()
23711     {
23712         this.fireEvent("beforeshow", this);
23713         
23714         this.hidden = false;
23715         this.el.addClass('open');
23716         
23717         Roo.get(document).on("mouseup", this.onMouseUp, this);
23718         
23719         this.fireEvent("show", this);
23720         
23721         
23722     },
23723     
23724     hide : function()
23725     {
23726         this.fireEvent("beforehide", this);
23727         
23728         this.hidden = true;
23729         this.el.removeClass('open');
23730         
23731         Roo.get(document).un("mouseup", this.onMouseUp);
23732         
23733         this.fireEvent("hide", this);
23734     },
23735     
23736     onMouseUp : function()
23737     {
23738         this.hide();
23739     }
23740     
23741 });
23742
23743  
23744  /*
23745  * - LGPL
23746  *
23747  * menu item
23748  * 
23749  */
23750 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23751
23752 /**
23753  * @class Roo.bootstrap.menu.Item
23754  * @extends Roo.bootstrap.Component
23755  * Bootstrap MenuItem class
23756  * @cfg {Boolean} submenu (true | false) default false
23757  * @cfg {String} html text of the item
23758  * @cfg {String} href the link
23759  * @cfg {Boolean} disable (true | false) default false
23760  * @cfg {Boolean} preventDefault (true | false) default true
23761  * @cfg {String} icon Font awesome icon
23762  * @cfg {String} pos Submenu align to (left | right) default right 
23763  * 
23764  * 
23765  * @constructor
23766  * Create a new Item
23767  * @param {Object} config The config object
23768  */
23769
23770
23771 Roo.bootstrap.menu.Item = function(config){
23772     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23773     this.addEvents({
23774         /**
23775          * @event mouseover
23776          * Fires when the mouse is hovering over this menu
23777          * @param {Roo.bootstrap.menu.Item} this
23778          * @param {Roo.EventObject} e
23779          */
23780         mouseover : true,
23781         /**
23782          * @event mouseout
23783          * Fires when the mouse exits this menu
23784          * @param {Roo.bootstrap.menu.Item} this
23785          * @param {Roo.EventObject} e
23786          */
23787         mouseout : true,
23788         // raw events
23789         /**
23790          * @event click
23791          * The raw click event for the entire grid.
23792          * @param {Roo.EventObject} e
23793          */
23794         click : true
23795     });
23796 };
23797
23798 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23799     
23800     submenu : false,
23801     href : '',
23802     html : '',
23803     preventDefault: true,
23804     disable : false,
23805     icon : false,
23806     pos : 'right',
23807     
23808     getAutoCreate : function()
23809     {
23810         var text = [
23811             {
23812                 tag : 'span',
23813                 cls : 'roo-menu-item-text',
23814                 html : this.html
23815             }
23816         ];
23817         
23818         if(this.icon){
23819             text.unshift({
23820                 tag : 'i',
23821                 cls : 'fa ' + this.icon
23822             })
23823         }
23824         
23825         var cfg = {
23826             tag : 'li',
23827             cn : [
23828                 {
23829                     tag : 'a',
23830                     href : this.href || '#',
23831                     cn : text
23832                 }
23833             ]
23834         };
23835         
23836         if(this.disable){
23837             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23838         }
23839         
23840         if(this.submenu){
23841             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23842             
23843             if(this.pos == 'left'){
23844                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23845             }
23846         }
23847         
23848         return cfg;
23849     },
23850     
23851     initEvents : function() 
23852     {
23853         this.el.on('mouseover', this.onMouseOver, this);
23854         this.el.on('mouseout', this.onMouseOut, this);
23855         
23856         this.el.select('a', true).first().on('click', this.onClick, this);
23857         
23858     },
23859     
23860     onClick : function(e)
23861     {
23862         if(this.preventDefault){
23863             e.preventDefault();
23864         }
23865         
23866         this.fireEvent("click", this, e);
23867     },
23868     
23869     onMouseOver : function(e)
23870     {
23871         if(this.submenu && this.pos == 'left'){
23872             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23873         }
23874         
23875         this.fireEvent("mouseover", this, e);
23876     },
23877     
23878     onMouseOut : function(e)
23879     {
23880         this.fireEvent("mouseout", this, e);
23881     }
23882 });
23883
23884  
23885
23886  /*
23887  * - LGPL
23888  *
23889  * menu separator
23890  * 
23891  */
23892 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23893
23894 /**
23895  * @class Roo.bootstrap.menu.Separator
23896  * @extends Roo.bootstrap.Component
23897  * Bootstrap Separator class
23898  * 
23899  * @constructor
23900  * Create a new Separator
23901  * @param {Object} config The config object
23902  */
23903
23904
23905 Roo.bootstrap.menu.Separator = function(config){
23906     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23907 };
23908
23909 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23910     
23911     getAutoCreate : function(){
23912         var cfg = {
23913             tag : 'li',
23914             cls: 'divider'
23915         };
23916         
23917         return cfg;
23918     }
23919    
23920 });
23921
23922  
23923
23924  /*
23925  * - LGPL
23926  *
23927  * Tooltip
23928  * 
23929  */
23930
23931 /**
23932  * @class Roo.bootstrap.Tooltip
23933  * Bootstrap Tooltip class
23934  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23935  * to determine which dom element triggers the tooltip.
23936  * 
23937  * It needs to add support for additional attributes like tooltip-position
23938  * 
23939  * @constructor
23940  * Create a new Toolti
23941  * @param {Object} config The config object
23942  */
23943
23944 Roo.bootstrap.Tooltip = function(config){
23945     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23946 };
23947
23948 Roo.apply(Roo.bootstrap.Tooltip, {
23949     /**
23950      * @function init initialize tooltip monitoring.
23951      * @static
23952      */
23953     currentEl : false,
23954     currentTip : false,
23955     currentRegion : false,
23956     
23957     //  init : delay?
23958     
23959     init : function()
23960     {
23961         Roo.get(document).on('mouseover', this.enter ,this);
23962         Roo.get(document).on('mouseout', this.leave, this);
23963          
23964         
23965         this.currentTip = new Roo.bootstrap.Tooltip();
23966     },
23967     
23968     enter : function(ev)
23969     {
23970         var dom = ev.getTarget();
23971         
23972         //Roo.log(['enter',dom]);
23973         var el = Roo.fly(dom);
23974         if (this.currentEl) {
23975             //Roo.log(dom);
23976             //Roo.log(this.currentEl);
23977             //Roo.log(this.currentEl.contains(dom));
23978             if (this.currentEl == el) {
23979                 return;
23980             }
23981             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23982                 return;
23983             }
23984
23985         }
23986         
23987         if (this.currentTip.el) {
23988             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23989         }    
23990         //Roo.log(ev);
23991         var bindEl = el;
23992         
23993         // you can not look for children, as if el is the body.. then everythign is the child..
23994         if (!el.attr('tooltip')) { //
23995             if (!el.select("[tooltip]").elements.length) {
23996                 return;
23997             }
23998             // is the mouse over this child...?
23999             bindEl = el.select("[tooltip]").first();
24000             var xy = ev.getXY();
24001             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24002                 //Roo.log("not in region.");
24003                 return;
24004             }
24005             //Roo.log("child element over..");
24006             
24007         }
24008         this.currentEl = bindEl;
24009         this.currentTip.bind(bindEl);
24010         this.currentRegion = Roo.lib.Region.getRegion(dom);
24011         this.currentTip.enter();
24012         
24013     },
24014     leave : function(ev)
24015     {
24016         var dom = ev.getTarget();
24017         //Roo.log(['leave',dom]);
24018         if (!this.currentEl) {
24019             return;
24020         }
24021         
24022         
24023         if (dom != this.currentEl.dom) {
24024             return;
24025         }
24026         var xy = ev.getXY();
24027         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24028             return;
24029         }
24030         // only activate leave if mouse cursor is outside... bounding box..
24031         
24032         
24033         
24034         
24035         if (this.currentTip) {
24036             this.currentTip.leave();
24037         }
24038         //Roo.log('clear currentEl');
24039         this.currentEl = false;
24040         
24041         
24042     },
24043     alignment : {
24044         'left' : ['r-l', [-2,0], 'right'],
24045         'right' : ['l-r', [2,0], 'left'],
24046         'bottom' : ['t-b', [0,2], 'top'],
24047         'top' : [ 'b-t', [0,-2], 'bottom']
24048     }
24049     
24050 });
24051
24052
24053 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24054     
24055     
24056     bindEl : false,
24057     
24058     delay : null, // can be { show : 300 , hide: 500}
24059     
24060     timeout : null,
24061     
24062     hoverState : null, //???
24063     
24064     placement : 'bottom', 
24065     
24066     getAutoCreate : function(){
24067     
24068         var cfg = {
24069            cls : 'tooltip',
24070            role : 'tooltip',
24071            cn : [
24072                 {
24073                     cls : 'tooltip-arrow'
24074                 },
24075                 {
24076                     cls : 'tooltip-inner'
24077                 }
24078            ]
24079         };
24080         
24081         return cfg;
24082     },
24083     bind : function(el)
24084     {
24085         this.bindEl = el;
24086     },
24087       
24088     
24089     enter : function () {
24090        
24091         if (this.timeout != null) {
24092             clearTimeout(this.timeout);
24093         }
24094         
24095         this.hoverState = 'in';
24096          //Roo.log("enter - show");
24097         if (!this.delay || !this.delay.show) {
24098             this.show();
24099             return;
24100         }
24101         var _t = this;
24102         this.timeout = setTimeout(function () {
24103             if (_t.hoverState == 'in') {
24104                 _t.show();
24105             }
24106         }, this.delay.show);
24107     },
24108     leave : function()
24109     {
24110         clearTimeout(this.timeout);
24111     
24112         this.hoverState = 'out';
24113          if (!this.delay || !this.delay.hide) {
24114             this.hide();
24115             return;
24116         }
24117        
24118         var _t = this;
24119         this.timeout = setTimeout(function () {
24120             //Roo.log("leave - timeout");
24121             
24122             if (_t.hoverState == 'out') {
24123                 _t.hide();
24124                 Roo.bootstrap.Tooltip.currentEl = false;
24125             }
24126         }, delay);
24127     },
24128     
24129     show : function ()
24130     {
24131         if (!this.el) {
24132             this.render(document.body);
24133         }
24134         // set content.
24135         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24136         
24137         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24138         
24139         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24140         
24141         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24142         
24143         var placement = typeof this.placement == 'function' ?
24144             this.placement.call(this, this.el, on_el) :
24145             this.placement;
24146             
24147         var autoToken = /\s?auto?\s?/i;
24148         var autoPlace = autoToken.test(placement);
24149         if (autoPlace) {
24150             placement = placement.replace(autoToken, '') || 'top';
24151         }
24152         
24153         //this.el.detach()
24154         //this.el.setXY([0,0]);
24155         this.el.show();
24156         //this.el.dom.style.display='block';
24157         
24158         //this.el.appendTo(on_el);
24159         
24160         var p = this.getPosition();
24161         var box = this.el.getBox();
24162         
24163         if (autoPlace) {
24164             // fixme..
24165         }
24166         
24167         var align = Roo.bootstrap.Tooltip.alignment[placement];
24168         
24169         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24170         
24171         if(placement == 'top' || placement == 'bottom'){
24172             if(xy[0] < 0){
24173                 placement = 'right';
24174             }
24175             
24176             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24177                 placement = 'left';
24178             }
24179         }
24180         
24181         align = Roo.bootstrap.Tooltip.alignment[placement];
24182         
24183         this.el.alignTo(this.bindEl, align[0],align[1]);
24184         //var arrow = this.el.select('.arrow',true).first();
24185         //arrow.set(align[2], 
24186         
24187         this.el.addClass(placement);
24188         
24189         this.el.addClass('in fade');
24190         
24191         this.hoverState = null;
24192         
24193         if (this.el.hasClass('fade')) {
24194             // fade it?
24195         }
24196         
24197     },
24198     hide : function()
24199     {
24200          
24201         if (!this.el) {
24202             return;
24203         }
24204         //this.el.setXY([0,0]);
24205         this.el.removeClass('in');
24206         //this.el.hide();
24207         
24208     }
24209     
24210 });
24211  
24212
24213  /*
24214  * - LGPL
24215  *
24216  * Location Picker
24217  * 
24218  */
24219
24220 /**
24221  * @class Roo.bootstrap.LocationPicker
24222  * @extends Roo.bootstrap.Component
24223  * Bootstrap LocationPicker class
24224  * @cfg {Number} latitude Position when init default 0
24225  * @cfg {Number} longitude Position when init default 0
24226  * @cfg {Number} zoom default 15
24227  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24228  * @cfg {Boolean} mapTypeControl default false
24229  * @cfg {Boolean} disableDoubleClickZoom default false
24230  * @cfg {Boolean} scrollwheel default true
24231  * @cfg {Boolean} streetViewControl default false
24232  * @cfg {Number} radius default 0
24233  * @cfg {String} locationName
24234  * @cfg {Boolean} draggable default true
24235  * @cfg {Boolean} enableAutocomplete default false
24236  * @cfg {Boolean} enableReverseGeocode default true
24237  * @cfg {String} markerTitle
24238  * 
24239  * @constructor
24240  * Create a new LocationPicker
24241  * @param {Object} config The config object
24242  */
24243
24244
24245 Roo.bootstrap.LocationPicker = function(config){
24246     
24247     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24248     
24249     this.addEvents({
24250         /**
24251          * @event initial
24252          * Fires when the picker initialized.
24253          * @param {Roo.bootstrap.LocationPicker} this
24254          * @param {Google Location} location
24255          */
24256         initial : true,
24257         /**
24258          * @event positionchanged
24259          * Fires when the picker position changed.
24260          * @param {Roo.bootstrap.LocationPicker} this
24261          * @param {Google Location} location
24262          */
24263         positionchanged : true,
24264         /**
24265          * @event resize
24266          * Fires when the map resize.
24267          * @param {Roo.bootstrap.LocationPicker} this
24268          */
24269         resize : true,
24270         /**
24271          * @event show
24272          * Fires when the map show.
24273          * @param {Roo.bootstrap.LocationPicker} this
24274          */
24275         show : true,
24276         /**
24277          * @event hide
24278          * Fires when the map hide.
24279          * @param {Roo.bootstrap.LocationPicker} this
24280          */
24281         hide : true,
24282         /**
24283          * @event mapClick
24284          * Fires when click the map.
24285          * @param {Roo.bootstrap.LocationPicker} this
24286          * @param {Map event} e
24287          */
24288         mapClick : true,
24289         /**
24290          * @event mapRightClick
24291          * Fires when right click the map.
24292          * @param {Roo.bootstrap.LocationPicker} this
24293          * @param {Map event} e
24294          */
24295         mapRightClick : true,
24296         /**
24297          * @event markerClick
24298          * Fires when click the marker.
24299          * @param {Roo.bootstrap.LocationPicker} this
24300          * @param {Map event} e
24301          */
24302         markerClick : true,
24303         /**
24304          * @event markerRightClick
24305          * Fires when right click the marker.
24306          * @param {Roo.bootstrap.LocationPicker} this
24307          * @param {Map event} e
24308          */
24309         markerRightClick : true,
24310         /**
24311          * @event OverlayViewDraw
24312          * Fires when OverlayView Draw
24313          * @param {Roo.bootstrap.LocationPicker} this
24314          */
24315         OverlayViewDraw : true,
24316         /**
24317          * @event OverlayViewOnAdd
24318          * Fires when OverlayView Draw
24319          * @param {Roo.bootstrap.LocationPicker} this
24320          */
24321         OverlayViewOnAdd : true,
24322         /**
24323          * @event OverlayViewOnRemove
24324          * Fires when OverlayView Draw
24325          * @param {Roo.bootstrap.LocationPicker} this
24326          */
24327         OverlayViewOnRemove : true,
24328         /**
24329          * @event OverlayViewShow
24330          * Fires when OverlayView Draw
24331          * @param {Roo.bootstrap.LocationPicker} this
24332          * @param {Pixel} cpx
24333          */
24334         OverlayViewShow : true,
24335         /**
24336          * @event OverlayViewHide
24337          * Fires when OverlayView Draw
24338          * @param {Roo.bootstrap.LocationPicker} this
24339          */
24340         OverlayViewHide : true,
24341         /**
24342          * @event loadexception
24343          * Fires when load google lib failed.
24344          * @param {Roo.bootstrap.LocationPicker} this
24345          */
24346         loadexception : true
24347     });
24348         
24349 };
24350
24351 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24352     
24353     gMapContext: false,
24354     
24355     latitude: 0,
24356     longitude: 0,
24357     zoom: 15,
24358     mapTypeId: false,
24359     mapTypeControl: false,
24360     disableDoubleClickZoom: false,
24361     scrollwheel: true,
24362     streetViewControl: false,
24363     radius: 0,
24364     locationName: '',
24365     draggable: true,
24366     enableAutocomplete: false,
24367     enableReverseGeocode: true,
24368     markerTitle: '',
24369     
24370     getAutoCreate: function()
24371     {
24372
24373         var cfg = {
24374             tag: 'div',
24375             cls: 'roo-location-picker'
24376         };
24377         
24378         return cfg
24379     },
24380     
24381     initEvents: function(ct, position)
24382     {       
24383         if(!this.el.getWidth() || this.isApplied()){
24384             return;
24385         }
24386         
24387         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24388         
24389         this.initial();
24390     },
24391     
24392     initial: function()
24393     {
24394         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24395             this.fireEvent('loadexception', this);
24396             return;
24397         }
24398         
24399         if(!this.mapTypeId){
24400             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24401         }
24402         
24403         this.gMapContext = this.GMapContext();
24404         
24405         this.initOverlayView();
24406         
24407         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24408         
24409         var _this = this;
24410                 
24411         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24412             _this.setPosition(_this.gMapContext.marker.position);
24413         });
24414         
24415         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24416             _this.fireEvent('mapClick', this, event);
24417             
24418         });
24419
24420         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24421             _this.fireEvent('mapRightClick', this, event);
24422             
24423         });
24424         
24425         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24426             _this.fireEvent('markerClick', this, event);
24427             
24428         });
24429
24430         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24431             _this.fireEvent('markerRightClick', this, event);
24432             
24433         });
24434         
24435         this.setPosition(this.gMapContext.location);
24436         
24437         this.fireEvent('initial', this, this.gMapContext.location);
24438     },
24439     
24440     initOverlayView: function()
24441     {
24442         var _this = this;
24443         
24444         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24445             
24446             draw: function()
24447             {
24448                 _this.fireEvent('OverlayViewDraw', _this);
24449             },
24450             
24451             onAdd: function()
24452             {
24453                 _this.fireEvent('OverlayViewOnAdd', _this);
24454             },
24455             
24456             onRemove: function()
24457             {
24458                 _this.fireEvent('OverlayViewOnRemove', _this);
24459             },
24460             
24461             show: function(cpx)
24462             {
24463                 _this.fireEvent('OverlayViewShow', _this, cpx);
24464             },
24465             
24466             hide: function()
24467             {
24468                 _this.fireEvent('OverlayViewHide', _this);
24469             }
24470             
24471         });
24472     },
24473     
24474     fromLatLngToContainerPixel: function(event)
24475     {
24476         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24477     },
24478     
24479     isApplied: function() 
24480     {
24481         return this.getGmapContext() == false ? false : true;
24482     },
24483     
24484     getGmapContext: function() 
24485     {
24486         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24487     },
24488     
24489     GMapContext: function() 
24490     {
24491         var position = new google.maps.LatLng(this.latitude, this.longitude);
24492         
24493         var _map = new google.maps.Map(this.el.dom, {
24494             center: position,
24495             zoom: this.zoom,
24496             mapTypeId: this.mapTypeId,
24497             mapTypeControl: this.mapTypeControl,
24498             disableDoubleClickZoom: this.disableDoubleClickZoom,
24499             scrollwheel: this.scrollwheel,
24500             streetViewControl: this.streetViewControl,
24501             locationName: this.locationName,
24502             draggable: this.draggable,
24503             enableAutocomplete: this.enableAutocomplete,
24504             enableReverseGeocode: this.enableReverseGeocode
24505         });
24506         
24507         var _marker = new google.maps.Marker({
24508             position: position,
24509             map: _map,
24510             title: this.markerTitle,
24511             draggable: this.draggable
24512         });
24513         
24514         return {
24515             map: _map,
24516             marker: _marker,
24517             circle: null,
24518             location: position,
24519             radius: this.radius,
24520             locationName: this.locationName,
24521             addressComponents: {
24522                 formatted_address: null,
24523                 addressLine1: null,
24524                 addressLine2: null,
24525                 streetName: null,
24526                 streetNumber: null,
24527                 city: null,
24528                 district: null,
24529                 state: null,
24530                 stateOrProvince: null
24531             },
24532             settings: this,
24533             domContainer: this.el.dom,
24534             geodecoder: new google.maps.Geocoder()
24535         };
24536     },
24537     
24538     drawCircle: function(center, radius, options) 
24539     {
24540         if (this.gMapContext.circle != null) {
24541             this.gMapContext.circle.setMap(null);
24542         }
24543         if (radius > 0) {
24544             radius *= 1;
24545             options = Roo.apply({}, options, {
24546                 strokeColor: "#0000FF",
24547                 strokeOpacity: .35,
24548                 strokeWeight: 2,
24549                 fillColor: "#0000FF",
24550                 fillOpacity: .2
24551             });
24552             
24553             options.map = this.gMapContext.map;
24554             options.radius = radius;
24555             options.center = center;
24556             this.gMapContext.circle = new google.maps.Circle(options);
24557             return this.gMapContext.circle;
24558         }
24559         
24560         return null;
24561     },
24562     
24563     setPosition: function(location) 
24564     {
24565         this.gMapContext.location = location;
24566         this.gMapContext.marker.setPosition(location);
24567         this.gMapContext.map.panTo(location);
24568         this.drawCircle(location, this.gMapContext.radius, {});
24569         
24570         var _this = this;
24571         
24572         if (this.gMapContext.settings.enableReverseGeocode) {
24573             this.gMapContext.geodecoder.geocode({
24574                 latLng: this.gMapContext.location
24575             }, function(results, status) {
24576                 
24577                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24578                     _this.gMapContext.locationName = results[0].formatted_address;
24579                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24580                     
24581                     _this.fireEvent('positionchanged', this, location);
24582                 }
24583             });
24584             
24585             return;
24586         }
24587         
24588         this.fireEvent('positionchanged', this, location);
24589     },
24590     
24591     resize: function()
24592     {
24593         google.maps.event.trigger(this.gMapContext.map, "resize");
24594         
24595         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24596         
24597         this.fireEvent('resize', this);
24598     },
24599     
24600     setPositionByLatLng: function(latitude, longitude)
24601     {
24602         this.setPosition(new google.maps.LatLng(latitude, longitude));
24603     },
24604     
24605     getCurrentPosition: function() 
24606     {
24607         return {
24608             latitude: this.gMapContext.location.lat(),
24609             longitude: this.gMapContext.location.lng()
24610         };
24611     },
24612     
24613     getAddressName: function() 
24614     {
24615         return this.gMapContext.locationName;
24616     },
24617     
24618     getAddressComponents: function() 
24619     {
24620         return this.gMapContext.addressComponents;
24621     },
24622     
24623     address_component_from_google_geocode: function(address_components) 
24624     {
24625         var result = {};
24626         
24627         for (var i = 0; i < address_components.length; i++) {
24628             var component = address_components[i];
24629             if (component.types.indexOf("postal_code") >= 0) {
24630                 result.postalCode = component.short_name;
24631             } else if (component.types.indexOf("street_number") >= 0) {
24632                 result.streetNumber = component.short_name;
24633             } else if (component.types.indexOf("route") >= 0) {
24634                 result.streetName = component.short_name;
24635             } else if (component.types.indexOf("neighborhood") >= 0) {
24636                 result.city = component.short_name;
24637             } else if (component.types.indexOf("locality") >= 0) {
24638                 result.city = component.short_name;
24639             } else if (component.types.indexOf("sublocality") >= 0) {
24640                 result.district = component.short_name;
24641             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24642                 result.stateOrProvince = component.short_name;
24643             } else if (component.types.indexOf("country") >= 0) {
24644                 result.country = component.short_name;
24645             }
24646         }
24647         
24648         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24649         result.addressLine2 = "";
24650         return result;
24651     },
24652     
24653     setZoomLevel: function(zoom)
24654     {
24655         this.gMapContext.map.setZoom(zoom);
24656     },
24657     
24658     show: function()
24659     {
24660         if(!this.el){
24661             return;
24662         }
24663         
24664         this.el.show();
24665         
24666         this.resize();
24667         
24668         this.fireEvent('show', this);
24669     },
24670     
24671     hide: function()
24672     {
24673         if(!this.el){
24674             return;
24675         }
24676         
24677         this.el.hide();
24678         
24679         this.fireEvent('hide', this);
24680     }
24681     
24682 });
24683
24684 Roo.apply(Roo.bootstrap.LocationPicker, {
24685     
24686     OverlayView : function(map, options)
24687     {
24688         options = options || {};
24689         
24690         this.setMap(map);
24691     }
24692     
24693     
24694 });/*
24695  * - LGPL
24696  *
24697  * Alert
24698  * 
24699  */
24700
24701 /**
24702  * @class Roo.bootstrap.Alert
24703  * @extends Roo.bootstrap.Component
24704  * Bootstrap Alert class
24705  * @cfg {String} title The title of alert
24706  * @cfg {String} html The content of alert
24707  * @cfg {String} weight (  success | info | warning | danger )
24708  * @cfg {String} faicon font-awesomeicon
24709  * 
24710  * @constructor
24711  * Create a new alert
24712  * @param {Object} config The config object
24713  */
24714
24715
24716 Roo.bootstrap.Alert = function(config){
24717     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24718     
24719 };
24720
24721 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24722     
24723     title: '',
24724     html: '',
24725     weight: false,
24726     faicon: false,
24727     
24728     getAutoCreate : function()
24729     {
24730         
24731         var cfg = {
24732             tag : 'div',
24733             cls : 'alert',
24734             cn : [
24735                 {
24736                     tag : 'i',
24737                     cls : 'roo-alert-icon'
24738                     
24739                 },
24740                 {
24741                     tag : 'b',
24742                     cls : 'roo-alert-title',
24743                     html : this.title
24744                 },
24745                 {
24746                     tag : 'span',
24747                     cls : 'roo-alert-text',
24748                     html : this.html
24749                 }
24750             ]
24751         };
24752         
24753         if(this.faicon){
24754             cfg.cn[0].cls += ' fa ' + this.faicon;
24755         }
24756         
24757         if(this.weight){
24758             cfg.cls += ' alert-' + this.weight;
24759         }
24760         
24761         return cfg;
24762     },
24763     
24764     initEvents: function() 
24765     {
24766         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24767     },
24768     
24769     setTitle : function(str)
24770     {
24771         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24772     },
24773     
24774     setText : function(str)
24775     {
24776         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24777     },
24778     
24779     setWeight : function(weight)
24780     {
24781         if(this.weight){
24782             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24783         }
24784         
24785         this.weight = weight;
24786         
24787         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24788     },
24789     
24790     setIcon : function(icon)
24791     {
24792         if(this.faicon){
24793             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24794         }
24795         
24796         this.faicon = icon;
24797         
24798         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24799     },
24800     
24801     hide: function() 
24802     {
24803         this.el.hide();   
24804     },
24805     
24806     show: function() 
24807     {  
24808         this.el.show();   
24809     }
24810     
24811 });
24812
24813  
24814 /*
24815 * Licence: LGPL
24816 */
24817
24818 /**
24819  * @class Roo.bootstrap.UploadCropbox
24820  * @extends Roo.bootstrap.Component
24821  * Bootstrap UploadCropbox class
24822  * @cfg {String} emptyText show when image has been loaded
24823  * @cfg {String} rotateNotify show when image too small to rotate
24824  * @cfg {Number} errorTimeout default 3000
24825  * @cfg {Number} minWidth default 300
24826  * @cfg {Number} minHeight default 300
24827  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24828  * @cfg {Boolean} isDocument (true|false) default false
24829  * @cfg {String} url action url
24830  * @cfg {String} paramName default 'imageUpload'
24831  * @cfg {String} method default POST
24832  * @cfg {Boolean} loadMask (true|false) default true
24833  * @cfg {Boolean} loadingText default 'Loading...'
24834  * 
24835  * @constructor
24836  * Create a new UploadCropbox
24837  * @param {Object} config The config object
24838  */
24839
24840 Roo.bootstrap.UploadCropbox = function(config){
24841     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24842     
24843     this.addEvents({
24844         /**
24845          * @event beforeselectfile
24846          * Fire before select file
24847          * @param {Roo.bootstrap.UploadCropbox} this
24848          */
24849         "beforeselectfile" : true,
24850         /**
24851          * @event initial
24852          * Fire after initEvent
24853          * @param {Roo.bootstrap.UploadCropbox} this
24854          */
24855         "initial" : true,
24856         /**
24857          * @event crop
24858          * Fire after initEvent
24859          * @param {Roo.bootstrap.UploadCropbox} this
24860          * @param {String} data
24861          */
24862         "crop" : true,
24863         /**
24864          * @event prepare
24865          * Fire when preparing the file data
24866          * @param {Roo.bootstrap.UploadCropbox} this
24867          * @param {Object} file
24868          */
24869         "prepare" : true,
24870         /**
24871          * @event exception
24872          * Fire when get exception
24873          * @param {Roo.bootstrap.UploadCropbox} this
24874          * @param {XMLHttpRequest} xhr
24875          */
24876         "exception" : true,
24877         /**
24878          * @event beforeloadcanvas
24879          * Fire before load the canvas
24880          * @param {Roo.bootstrap.UploadCropbox} this
24881          * @param {String} src
24882          */
24883         "beforeloadcanvas" : true,
24884         /**
24885          * @event trash
24886          * Fire when trash image
24887          * @param {Roo.bootstrap.UploadCropbox} this
24888          */
24889         "trash" : true,
24890         /**
24891          * @event download
24892          * Fire when download the image
24893          * @param {Roo.bootstrap.UploadCropbox} this
24894          */
24895         "download" : true,
24896         /**
24897          * @event footerbuttonclick
24898          * Fire when footerbuttonclick
24899          * @param {Roo.bootstrap.UploadCropbox} this
24900          * @param {String} type
24901          */
24902         "footerbuttonclick" : true,
24903         /**
24904          * @event resize
24905          * Fire when resize
24906          * @param {Roo.bootstrap.UploadCropbox} this
24907          */
24908         "resize" : true,
24909         /**
24910          * @event rotate
24911          * Fire when rotate the image
24912          * @param {Roo.bootstrap.UploadCropbox} this
24913          * @param {String} pos
24914          */
24915         "rotate" : true,
24916         /**
24917          * @event inspect
24918          * Fire when inspect the file
24919          * @param {Roo.bootstrap.UploadCropbox} this
24920          * @param {Object} file
24921          */
24922         "inspect" : true,
24923         /**
24924          * @event upload
24925          * Fire when xhr upload the file
24926          * @param {Roo.bootstrap.UploadCropbox} this
24927          * @param {Object} data
24928          */
24929         "upload" : true,
24930         /**
24931          * @event arrange
24932          * Fire when arrange the file data
24933          * @param {Roo.bootstrap.UploadCropbox} this
24934          * @param {Object} formData
24935          */
24936         "arrange" : true
24937     });
24938     
24939     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24940 };
24941
24942 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24943     
24944     emptyText : 'Click to upload image',
24945     rotateNotify : 'Image is too small to rotate',
24946     errorTimeout : 3000,
24947     scale : 0,
24948     baseScale : 1,
24949     rotate : 0,
24950     dragable : false,
24951     pinching : false,
24952     mouseX : 0,
24953     mouseY : 0,
24954     cropData : false,
24955     minWidth : 300,
24956     minHeight : 300,
24957     file : false,
24958     exif : {},
24959     baseRotate : 1,
24960     cropType : 'image/jpeg',
24961     buttons : false,
24962     canvasLoaded : false,
24963     isDocument : false,
24964     method : 'POST',
24965     paramName : 'imageUpload',
24966     loadMask : true,
24967     loadingText : 'Loading...',
24968     maskEl : false,
24969     
24970     getAutoCreate : function()
24971     {
24972         var cfg = {
24973             tag : 'div',
24974             cls : 'roo-upload-cropbox',
24975             cn : [
24976                 {
24977                     tag : 'input',
24978                     cls : 'roo-upload-cropbox-selector',
24979                     type : 'file'
24980                 },
24981                 {
24982                     tag : 'div',
24983                     cls : 'roo-upload-cropbox-body',
24984                     style : 'cursor:pointer',
24985                     cn : [
24986                         {
24987                             tag : 'div',
24988                             cls : 'roo-upload-cropbox-preview'
24989                         },
24990                         {
24991                             tag : 'div',
24992                             cls : 'roo-upload-cropbox-thumb'
24993                         },
24994                         {
24995                             tag : 'div',
24996                             cls : 'roo-upload-cropbox-empty-notify',
24997                             html : this.emptyText
24998                         },
24999                         {
25000                             tag : 'div',
25001                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25002                             html : this.rotateNotify
25003                         }
25004                     ]
25005                 },
25006                 {
25007                     tag : 'div',
25008                     cls : 'roo-upload-cropbox-footer',
25009                     cn : {
25010                         tag : 'div',
25011                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25012                         cn : []
25013                     }
25014                 }
25015             ]
25016         };
25017         
25018         return cfg;
25019     },
25020     
25021     onRender : function(ct, position)
25022     {
25023         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25024         
25025         if (this.buttons.length) {
25026             
25027             Roo.each(this.buttons, function(bb) {
25028                 
25029                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25030                 
25031                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25032                 
25033             }, this);
25034         }
25035         
25036         if(this.loadMask){
25037             this.maskEl = this.el;
25038         }
25039     },
25040     
25041     initEvents : function()
25042     {
25043         this.urlAPI = (window.createObjectURL && window) || 
25044                                 (window.URL && URL.revokeObjectURL && URL) || 
25045                                 (window.webkitURL && webkitURL);
25046                         
25047         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25048         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25049         
25050         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25051         this.selectorEl.hide();
25052         
25053         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25054         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25055         
25056         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25057         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25058         this.thumbEl.hide();
25059         
25060         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25061         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25062         
25063         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25064         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25065         this.errorEl.hide();
25066         
25067         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25068         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25069         this.footerEl.hide();
25070         
25071         this.setThumbBoxSize();
25072         
25073         this.bind();
25074         
25075         this.resize();
25076         
25077         this.fireEvent('initial', this);
25078     },
25079
25080     bind : function()
25081     {
25082         var _this = this;
25083         
25084         window.addEventListener("resize", function() { _this.resize(); } );
25085         
25086         this.bodyEl.on('click', this.beforeSelectFile, this);
25087         
25088         if(Roo.isTouch){
25089             this.bodyEl.on('touchstart', this.onTouchStart, this);
25090             this.bodyEl.on('touchmove', this.onTouchMove, this);
25091             this.bodyEl.on('touchend', this.onTouchEnd, this);
25092         }
25093         
25094         if(!Roo.isTouch){
25095             this.bodyEl.on('mousedown', this.onMouseDown, this);
25096             this.bodyEl.on('mousemove', this.onMouseMove, this);
25097             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25098             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25099             Roo.get(document).on('mouseup', this.onMouseUp, this);
25100         }
25101         
25102         this.selectorEl.on('change', this.onFileSelected, this);
25103     },
25104     
25105     reset : function()
25106     {    
25107         this.scale = 0;
25108         this.baseScale = 1;
25109         this.rotate = 0;
25110         this.baseRotate = 1;
25111         this.dragable = false;
25112         this.pinching = false;
25113         this.mouseX = 0;
25114         this.mouseY = 0;
25115         this.cropData = false;
25116         this.notifyEl.dom.innerHTML = this.emptyText;
25117         
25118         this.selectorEl.dom.value = '';
25119         
25120     },
25121     
25122     resize : function()
25123     {
25124         if(this.fireEvent('resize', this) != false){
25125             this.setThumbBoxPosition();
25126             this.setCanvasPosition();
25127         }
25128     },
25129     
25130     onFooterButtonClick : function(e, el, o, type)
25131     {
25132         switch (type) {
25133             case 'rotate-left' :
25134                 this.onRotateLeft(e);
25135                 break;
25136             case 'rotate-right' :
25137                 this.onRotateRight(e);
25138                 break;
25139             case 'picture' :
25140                 this.beforeSelectFile(e);
25141                 break;
25142             case 'trash' :
25143                 this.trash(e);
25144                 break;
25145             case 'crop' :
25146                 this.crop(e);
25147                 break;
25148             case 'download' :
25149                 this.download(e);
25150                 break;
25151             default :
25152                 break;
25153         }
25154         
25155         this.fireEvent('footerbuttonclick', this, type);
25156     },
25157     
25158     beforeSelectFile : function(e)
25159     {
25160         e.preventDefault();
25161         
25162         if(this.fireEvent('beforeselectfile', this) != false){
25163             this.selectorEl.dom.click();
25164         }
25165     },
25166     
25167     onFileSelected : function(e)
25168     {
25169         e.preventDefault();
25170         
25171         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25172             return;
25173         }
25174         
25175         var file = this.selectorEl.dom.files[0];
25176         
25177         if(this.fireEvent('inspect', this, file) != false){
25178             this.prepare(file);
25179         }
25180         
25181     },
25182     
25183     trash : function(e)
25184     {
25185         this.fireEvent('trash', this);
25186     },
25187     
25188     download : function(e)
25189     {
25190         this.fireEvent('download', this);
25191     },
25192     
25193     loadCanvas : function(src)
25194     {   
25195         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25196             
25197             this.reset();
25198             
25199             this.imageEl = document.createElement('img');
25200             
25201             var _this = this;
25202             
25203             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25204             
25205             this.imageEl.src = src;
25206         }
25207     },
25208     
25209     onLoadCanvas : function()
25210     {   
25211         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25212         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25213         
25214         this.bodyEl.un('click', this.beforeSelectFile, this);
25215         
25216         this.notifyEl.hide();
25217         this.thumbEl.show();
25218         this.footerEl.show();
25219         
25220         this.baseRotateLevel();
25221         
25222         if(this.isDocument){
25223             this.setThumbBoxSize();
25224         }
25225         
25226         this.setThumbBoxPosition();
25227         
25228         this.baseScaleLevel();
25229         
25230         this.draw();
25231         
25232         this.resize();
25233         
25234         this.canvasLoaded = true;
25235         
25236         if(this.loadMask){
25237             this.maskEl.unmask();
25238         }
25239         
25240     },
25241     
25242     setCanvasPosition : function()
25243     {   
25244         if(!this.canvasEl){
25245             return;
25246         }
25247         
25248         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25249         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25250         
25251         this.previewEl.setLeft(pw);
25252         this.previewEl.setTop(ph);
25253         
25254     },
25255     
25256     onMouseDown : function(e)
25257     {   
25258         e.stopEvent();
25259         
25260         this.dragable = true;
25261         this.pinching = false;
25262         
25263         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25264             this.dragable = false;
25265             return;
25266         }
25267         
25268         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25269         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25270         
25271     },
25272     
25273     onMouseMove : function(e)
25274     {   
25275         e.stopEvent();
25276         
25277         if(!this.canvasLoaded){
25278             return;
25279         }
25280         
25281         if (!this.dragable){
25282             return;
25283         }
25284         
25285         var minX = Math.ceil(this.thumbEl.getLeft(true));
25286         var minY = Math.ceil(this.thumbEl.getTop(true));
25287         
25288         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25289         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25290         
25291         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25292         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25293         
25294         x = x - this.mouseX;
25295         y = y - this.mouseY;
25296         
25297         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25298         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25299         
25300         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25301         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25302         
25303         this.previewEl.setLeft(bgX);
25304         this.previewEl.setTop(bgY);
25305         
25306         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25307         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25308     },
25309     
25310     onMouseUp : function(e)
25311     {   
25312         e.stopEvent();
25313         
25314         this.dragable = false;
25315     },
25316     
25317     onMouseWheel : function(e)
25318     {   
25319         e.stopEvent();
25320         
25321         this.startScale = this.scale;
25322         
25323         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25324         
25325         if(!this.zoomable()){
25326             this.scale = this.startScale;
25327             return;
25328         }
25329         
25330         this.draw();
25331         
25332         return;
25333     },
25334     
25335     zoomable : function()
25336     {
25337         var minScale = this.thumbEl.getWidth() / this.minWidth;
25338         
25339         if(this.minWidth < this.minHeight){
25340             minScale = this.thumbEl.getHeight() / this.minHeight;
25341         }
25342         
25343         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25344         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25345         
25346         if(
25347                 this.isDocument &&
25348                 (this.rotate == 0 || this.rotate == 180) && 
25349                 (
25350                     width > this.imageEl.OriginWidth || 
25351                     height > this.imageEl.OriginHeight ||
25352                     (width < this.minWidth && height < this.minHeight)
25353                 )
25354         ){
25355             return false;
25356         }
25357         
25358         if(
25359                 this.isDocument &&
25360                 (this.rotate == 90 || this.rotate == 270) && 
25361                 (
25362                     width > this.imageEl.OriginWidth || 
25363                     height > this.imageEl.OriginHeight ||
25364                     (width < this.minHeight && height < this.minWidth)
25365                 )
25366         ){
25367             return false;
25368         }
25369         
25370         if(
25371                 !this.isDocument &&
25372                 (this.rotate == 0 || this.rotate == 180) && 
25373                 (
25374                     width < this.minWidth || 
25375                     width > this.imageEl.OriginWidth || 
25376                     height < this.minHeight || 
25377                     height > this.imageEl.OriginHeight
25378                 )
25379         ){
25380             return false;
25381         }
25382         
25383         if(
25384                 !this.isDocument &&
25385                 (this.rotate == 90 || this.rotate == 270) && 
25386                 (
25387                     width < this.minHeight || 
25388                     width > this.imageEl.OriginWidth || 
25389                     height < this.minWidth || 
25390                     height > this.imageEl.OriginHeight
25391                 )
25392         ){
25393             return false;
25394         }
25395         
25396         return true;
25397         
25398     },
25399     
25400     onRotateLeft : function(e)
25401     {   
25402         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25403             
25404             var minScale = this.thumbEl.getWidth() / this.minWidth;
25405             
25406             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25407             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25408             
25409             this.startScale = this.scale;
25410             
25411             while (this.getScaleLevel() < minScale){
25412             
25413                 this.scale = this.scale + 1;
25414                 
25415                 if(!this.zoomable()){
25416                     break;
25417                 }
25418                 
25419                 if(
25420                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25421                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25422                 ){
25423                     continue;
25424                 }
25425                 
25426                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25427
25428                 this.draw();
25429                 
25430                 return;
25431             }
25432             
25433             this.scale = this.startScale;
25434             
25435             this.onRotateFail();
25436             
25437             return false;
25438         }
25439         
25440         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25441
25442         if(this.isDocument){
25443             this.setThumbBoxSize();
25444             this.setThumbBoxPosition();
25445             this.setCanvasPosition();
25446         }
25447         
25448         this.draw();
25449         
25450         this.fireEvent('rotate', this, 'left');
25451         
25452     },
25453     
25454     onRotateRight : function(e)
25455     {
25456         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25457             
25458             var minScale = this.thumbEl.getWidth() / this.minWidth;
25459         
25460             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25461             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25462             
25463             this.startScale = this.scale;
25464             
25465             while (this.getScaleLevel() < minScale){
25466             
25467                 this.scale = this.scale + 1;
25468                 
25469                 if(!this.zoomable()){
25470                     break;
25471                 }
25472                 
25473                 if(
25474                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25475                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25476                 ){
25477                     continue;
25478                 }
25479                 
25480                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25481
25482                 this.draw();
25483                 
25484                 return;
25485             }
25486             
25487             this.scale = this.startScale;
25488             
25489             this.onRotateFail();
25490             
25491             return false;
25492         }
25493         
25494         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25495
25496         if(this.isDocument){
25497             this.setThumbBoxSize();
25498             this.setThumbBoxPosition();
25499             this.setCanvasPosition();
25500         }
25501         
25502         this.draw();
25503         
25504         this.fireEvent('rotate', this, 'right');
25505     },
25506     
25507     onRotateFail : function()
25508     {
25509         this.errorEl.show(true);
25510         
25511         var _this = this;
25512         
25513         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25514     },
25515     
25516     draw : function()
25517     {
25518         this.previewEl.dom.innerHTML = '';
25519         
25520         var canvasEl = document.createElement("canvas");
25521         
25522         var contextEl = canvasEl.getContext("2d");
25523         
25524         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25525         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25526         var center = this.imageEl.OriginWidth / 2;
25527         
25528         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25529             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25530             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25531             center = this.imageEl.OriginHeight / 2;
25532         }
25533         
25534         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25535         
25536         contextEl.translate(center, center);
25537         contextEl.rotate(this.rotate * Math.PI / 180);
25538
25539         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25540         
25541         this.canvasEl = document.createElement("canvas");
25542         
25543         this.contextEl = this.canvasEl.getContext("2d");
25544         
25545         switch (this.rotate) {
25546             case 0 :
25547                 
25548                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25549                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25550                 
25551                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25552                 
25553                 break;
25554             case 90 : 
25555                 
25556                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25557                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25558                 
25559                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25560                     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);
25561                     break;
25562                 }
25563                 
25564                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25565                 
25566                 break;
25567             case 180 :
25568                 
25569                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25570                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25571                 
25572                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25573                     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);
25574                     break;
25575                 }
25576                 
25577                 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);
25578                 
25579                 break;
25580             case 270 :
25581                 
25582                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25583                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25584         
25585                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25586                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25587                     break;
25588                 }
25589                 
25590                 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);
25591                 
25592                 break;
25593             default : 
25594                 break;
25595         }
25596         
25597         this.previewEl.appendChild(this.canvasEl);
25598         
25599         this.setCanvasPosition();
25600     },
25601     
25602     crop : function()
25603     {
25604         if(!this.canvasLoaded){
25605             return;
25606         }
25607         
25608         var imageCanvas = document.createElement("canvas");
25609         
25610         var imageContext = imageCanvas.getContext("2d");
25611         
25612         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25613         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25614         
25615         var center = imageCanvas.width / 2;
25616         
25617         imageContext.translate(center, center);
25618         
25619         imageContext.rotate(this.rotate * Math.PI / 180);
25620         
25621         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25622         
25623         var canvas = document.createElement("canvas");
25624         
25625         var context = canvas.getContext("2d");
25626                 
25627         canvas.width = this.minWidth;
25628         canvas.height = this.minHeight;
25629
25630         switch (this.rotate) {
25631             case 0 :
25632                 
25633                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25634                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25635                 
25636                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25637                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25638                 
25639                 var targetWidth = this.minWidth - 2 * x;
25640                 var targetHeight = this.minHeight - 2 * y;
25641                 
25642                 var scale = 1;
25643                 
25644                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25645                     scale = targetWidth / width;
25646                 }
25647                 
25648                 if(x > 0 && y == 0){
25649                     scale = targetHeight / height;
25650                 }
25651                 
25652                 if(x > 0 && y > 0){
25653                     scale = targetWidth / width;
25654                     
25655                     if(width < height){
25656                         scale = targetHeight / height;
25657                     }
25658                 }
25659                 
25660                 context.scale(scale, scale);
25661                 
25662                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25663                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25664
25665                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25666                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25667
25668                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25669                 
25670                 break;
25671             case 90 : 
25672                 
25673                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25674                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25675                 
25676                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25677                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25678                 
25679                 var targetWidth = this.minWidth - 2 * x;
25680                 var targetHeight = this.minHeight - 2 * y;
25681                 
25682                 var scale = 1;
25683                 
25684                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25685                     scale = targetWidth / width;
25686                 }
25687                 
25688                 if(x > 0 && y == 0){
25689                     scale = targetHeight / height;
25690                 }
25691                 
25692                 if(x > 0 && y > 0){
25693                     scale = targetWidth / width;
25694                     
25695                     if(width < height){
25696                         scale = targetHeight / height;
25697                     }
25698                 }
25699                 
25700                 context.scale(scale, scale);
25701                 
25702                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25703                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25704
25705                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25706                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25707                 
25708                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25709                 
25710                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25711                 
25712                 break;
25713             case 180 :
25714                 
25715                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25716                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25717                 
25718                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25719                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25720                 
25721                 var targetWidth = this.minWidth - 2 * x;
25722                 var targetHeight = this.minHeight - 2 * y;
25723                 
25724                 var scale = 1;
25725                 
25726                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25727                     scale = targetWidth / width;
25728                 }
25729                 
25730                 if(x > 0 && y == 0){
25731                     scale = targetHeight / height;
25732                 }
25733                 
25734                 if(x > 0 && y > 0){
25735                     scale = targetWidth / width;
25736                     
25737                     if(width < height){
25738                         scale = targetHeight / height;
25739                     }
25740                 }
25741                 
25742                 context.scale(scale, scale);
25743                 
25744                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25745                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25746
25747                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25748                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25749
25750                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25751                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25752                 
25753                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25754                 
25755                 break;
25756             case 270 :
25757                 
25758                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25759                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25760                 
25761                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25762                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25763                 
25764                 var targetWidth = this.minWidth - 2 * x;
25765                 var targetHeight = this.minHeight - 2 * y;
25766                 
25767                 var scale = 1;
25768                 
25769                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25770                     scale = targetWidth / width;
25771                 }
25772                 
25773                 if(x > 0 && y == 0){
25774                     scale = targetHeight / height;
25775                 }
25776                 
25777                 if(x > 0 && y > 0){
25778                     scale = targetWidth / width;
25779                     
25780                     if(width < height){
25781                         scale = targetHeight / height;
25782                     }
25783                 }
25784                 
25785                 context.scale(scale, scale);
25786                 
25787                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25788                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25789
25790                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25791                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25792                 
25793                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25794                 
25795                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25796                 
25797                 break;
25798             default : 
25799                 break;
25800         }
25801         
25802         this.cropData = canvas.toDataURL(this.cropType);
25803         
25804         if(this.fireEvent('crop', this, this.cropData) !== false){
25805             this.process(this.file, this.cropData);
25806         }
25807         
25808         return;
25809         
25810     },
25811     
25812     setThumbBoxSize : function()
25813     {
25814         var width, height;
25815         
25816         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25817             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25818             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25819             
25820             this.minWidth = width;
25821             this.minHeight = height;
25822             
25823             if(this.rotate == 90 || this.rotate == 270){
25824                 this.minWidth = height;
25825                 this.minHeight = width;
25826             }
25827         }
25828         
25829         height = 300;
25830         width = Math.ceil(this.minWidth * height / this.minHeight);
25831         
25832         if(this.minWidth > this.minHeight){
25833             width = 300;
25834             height = Math.ceil(this.minHeight * width / this.minWidth);
25835         }
25836         
25837         this.thumbEl.setStyle({
25838             width : width + 'px',
25839             height : height + 'px'
25840         });
25841
25842         return;
25843             
25844     },
25845     
25846     setThumbBoxPosition : function()
25847     {
25848         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25849         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25850         
25851         this.thumbEl.setLeft(x);
25852         this.thumbEl.setTop(y);
25853         
25854     },
25855     
25856     baseRotateLevel : function()
25857     {
25858         this.baseRotate = 1;
25859         
25860         if(
25861                 typeof(this.exif) != 'undefined' &&
25862                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25863                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25864         ){
25865             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25866         }
25867         
25868         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25869         
25870     },
25871     
25872     baseScaleLevel : function()
25873     {
25874         var width, height;
25875         
25876         if(this.isDocument){
25877             
25878             if(this.baseRotate == 6 || this.baseRotate == 8){
25879             
25880                 height = this.thumbEl.getHeight();
25881                 this.baseScale = height / this.imageEl.OriginWidth;
25882
25883                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25884                     width = this.thumbEl.getWidth();
25885                     this.baseScale = width / this.imageEl.OriginHeight;
25886                 }
25887
25888                 return;
25889             }
25890
25891             height = this.thumbEl.getHeight();
25892             this.baseScale = height / this.imageEl.OriginHeight;
25893
25894             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25895                 width = this.thumbEl.getWidth();
25896                 this.baseScale = width / this.imageEl.OriginWidth;
25897             }
25898
25899             return;
25900         }
25901         
25902         if(this.baseRotate == 6 || this.baseRotate == 8){
25903             
25904             width = this.thumbEl.getHeight();
25905             this.baseScale = width / this.imageEl.OriginHeight;
25906             
25907             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25908                 height = this.thumbEl.getWidth();
25909                 this.baseScale = height / this.imageEl.OriginHeight;
25910             }
25911             
25912             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25913                 height = this.thumbEl.getWidth();
25914                 this.baseScale = height / this.imageEl.OriginHeight;
25915                 
25916                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25917                     width = this.thumbEl.getHeight();
25918                     this.baseScale = width / this.imageEl.OriginWidth;
25919                 }
25920             }
25921             
25922             return;
25923         }
25924         
25925         width = this.thumbEl.getWidth();
25926         this.baseScale = width / this.imageEl.OriginWidth;
25927         
25928         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25929             height = this.thumbEl.getHeight();
25930             this.baseScale = height / this.imageEl.OriginHeight;
25931         }
25932         
25933         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25934             
25935             height = this.thumbEl.getHeight();
25936             this.baseScale = height / this.imageEl.OriginHeight;
25937             
25938             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25939                 width = this.thumbEl.getWidth();
25940                 this.baseScale = width / this.imageEl.OriginWidth;
25941             }
25942             
25943         }
25944         
25945         return;
25946     },
25947     
25948     getScaleLevel : function()
25949     {
25950         return this.baseScale * Math.pow(1.1, this.scale);
25951     },
25952     
25953     onTouchStart : function(e)
25954     {
25955         if(!this.canvasLoaded){
25956             this.beforeSelectFile(e);
25957             return;
25958         }
25959         
25960         var touches = e.browserEvent.touches;
25961         
25962         if(!touches){
25963             return;
25964         }
25965         
25966         if(touches.length == 1){
25967             this.onMouseDown(e);
25968             return;
25969         }
25970         
25971         if(touches.length != 2){
25972             return;
25973         }
25974         
25975         var coords = [];
25976         
25977         for(var i = 0, finger; finger = touches[i]; i++){
25978             coords.push(finger.pageX, finger.pageY);
25979         }
25980         
25981         var x = Math.pow(coords[0] - coords[2], 2);
25982         var y = Math.pow(coords[1] - coords[3], 2);
25983         
25984         this.startDistance = Math.sqrt(x + y);
25985         
25986         this.startScale = this.scale;
25987         
25988         this.pinching = true;
25989         this.dragable = false;
25990         
25991     },
25992     
25993     onTouchMove : function(e)
25994     {
25995         if(!this.pinching && !this.dragable){
25996             return;
25997         }
25998         
25999         var touches = e.browserEvent.touches;
26000         
26001         if(!touches){
26002             return;
26003         }
26004         
26005         if(this.dragable){
26006             this.onMouseMove(e);
26007             return;
26008         }
26009         
26010         var coords = [];
26011         
26012         for(var i = 0, finger; finger = touches[i]; i++){
26013             coords.push(finger.pageX, finger.pageY);
26014         }
26015         
26016         var x = Math.pow(coords[0] - coords[2], 2);
26017         var y = Math.pow(coords[1] - coords[3], 2);
26018         
26019         this.endDistance = Math.sqrt(x + y);
26020         
26021         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26022         
26023         if(!this.zoomable()){
26024             this.scale = this.startScale;
26025             return;
26026         }
26027         
26028         this.draw();
26029         
26030     },
26031     
26032     onTouchEnd : function(e)
26033     {
26034         this.pinching = false;
26035         this.dragable = false;
26036         
26037     },
26038     
26039     process : function(file, crop)
26040     {
26041         if(this.loadMask){
26042             this.maskEl.mask(this.loadingText);
26043         }
26044         
26045         this.xhr = new XMLHttpRequest();
26046         
26047         file.xhr = this.xhr;
26048
26049         this.xhr.open(this.method, this.url, true);
26050         
26051         var headers = {
26052             "Accept": "application/json",
26053             "Cache-Control": "no-cache",
26054             "X-Requested-With": "XMLHttpRequest"
26055         };
26056         
26057         for (var headerName in headers) {
26058             var headerValue = headers[headerName];
26059             if (headerValue) {
26060                 this.xhr.setRequestHeader(headerName, headerValue);
26061             }
26062         }
26063         
26064         var _this = this;
26065         
26066         this.xhr.onload = function()
26067         {
26068             _this.xhrOnLoad(_this.xhr);
26069         }
26070         
26071         this.xhr.onerror = function()
26072         {
26073             _this.xhrOnError(_this.xhr);
26074         }
26075         
26076         var formData = new FormData();
26077
26078         formData.append('returnHTML', 'NO');
26079         
26080         if(crop){
26081             formData.append('crop', crop);
26082         }
26083         
26084         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26085             formData.append(this.paramName, file, file.name);
26086         }
26087         
26088         if(typeof(file.filename) != 'undefined'){
26089             formData.append('filename', file.filename);
26090         }
26091         
26092         if(typeof(file.mimetype) != 'undefined'){
26093             formData.append('mimetype', file.mimetype);
26094         }
26095         
26096         if(this.fireEvent('arrange', this, formData) != false){
26097             this.xhr.send(formData);
26098         };
26099     },
26100     
26101     xhrOnLoad : function(xhr)
26102     {
26103         if(this.loadMask){
26104             this.maskEl.unmask();
26105         }
26106         
26107         if (xhr.readyState !== 4) {
26108             this.fireEvent('exception', this, xhr);
26109             return;
26110         }
26111
26112         var response = Roo.decode(xhr.responseText);
26113         
26114         if(!response.success){
26115             this.fireEvent('exception', this, xhr);
26116             return;
26117         }
26118         
26119         var response = Roo.decode(xhr.responseText);
26120         
26121         this.fireEvent('upload', this, response);
26122         
26123     },
26124     
26125     xhrOnError : function()
26126     {
26127         if(this.loadMask){
26128             this.maskEl.unmask();
26129         }
26130         
26131         Roo.log('xhr on error');
26132         
26133         var response = Roo.decode(xhr.responseText);
26134           
26135         Roo.log(response);
26136         
26137     },
26138     
26139     prepare : function(file)
26140     {   
26141         if(this.loadMask){
26142             this.maskEl.mask(this.loadingText);
26143         }
26144         
26145         this.file = false;
26146         this.exif = {};
26147         
26148         if(typeof(file) === 'string'){
26149             this.loadCanvas(file);
26150             return;
26151         }
26152         
26153         if(!file || !this.urlAPI){
26154             return;
26155         }
26156         
26157         this.file = file;
26158         this.cropType = file.type;
26159         
26160         var _this = this;
26161         
26162         if(this.fireEvent('prepare', this, this.file) != false){
26163             
26164             var reader = new FileReader();
26165             
26166             reader.onload = function (e) {
26167                 if (e.target.error) {
26168                     Roo.log(e.target.error);
26169                     return;
26170                 }
26171                 
26172                 var buffer = e.target.result,
26173                     dataView = new DataView(buffer),
26174                     offset = 2,
26175                     maxOffset = dataView.byteLength - 4,
26176                     markerBytes,
26177                     markerLength;
26178                 
26179                 if (dataView.getUint16(0) === 0xffd8) {
26180                     while (offset < maxOffset) {
26181                         markerBytes = dataView.getUint16(offset);
26182                         
26183                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26184                             markerLength = dataView.getUint16(offset + 2) + 2;
26185                             if (offset + markerLength > dataView.byteLength) {
26186                                 Roo.log('Invalid meta data: Invalid segment size.');
26187                                 break;
26188                             }
26189                             
26190                             if(markerBytes == 0xffe1){
26191                                 _this.parseExifData(
26192                                     dataView,
26193                                     offset,
26194                                     markerLength
26195                                 );
26196                             }
26197                             
26198                             offset += markerLength;
26199                             
26200                             continue;
26201                         }
26202                         
26203                         break;
26204                     }
26205                     
26206                 }
26207                 
26208                 var url = _this.urlAPI.createObjectURL(_this.file);
26209                 
26210                 _this.loadCanvas(url);
26211                 
26212                 return;
26213             }
26214             
26215             reader.readAsArrayBuffer(this.file);
26216             
26217         }
26218         
26219     },
26220     
26221     parseExifData : function(dataView, offset, length)
26222     {
26223         var tiffOffset = offset + 10,
26224             littleEndian,
26225             dirOffset;
26226     
26227         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26228             // No Exif data, might be XMP data instead
26229             return;
26230         }
26231         
26232         // Check for the ASCII code for "Exif" (0x45786966):
26233         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26234             // No Exif data, might be XMP data instead
26235             return;
26236         }
26237         if (tiffOffset + 8 > dataView.byteLength) {
26238             Roo.log('Invalid Exif data: Invalid segment size.');
26239             return;
26240         }
26241         // Check for the two null bytes:
26242         if (dataView.getUint16(offset + 8) !== 0x0000) {
26243             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26244             return;
26245         }
26246         // Check the byte alignment:
26247         switch (dataView.getUint16(tiffOffset)) {
26248         case 0x4949:
26249             littleEndian = true;
26250             break;
26251         case 0x4D4D:
26252             littleEndian = false;
26253             break;
26254         default:
26255             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26256             return;
26257         }
26258         // Check for the TIFF tag marker (0x002A):
26259         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26260             Roo.log('Invalid Exif data: Missing TIFF marker.');
26261             return;
26262         }
26263         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26264         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26265         
26266         this.parseExifTags(
26267             dataView,
26268             tiffOffset,
26269             tiffOffset + dirOffset,
26270             littleEndian
26271         );
26272     },
26273     
26274     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26275     {
26276         var tagsNumber,
26277             dirEndOffset,
26278             i;
26279         if (dirOffset + 6 > dataView.byteLength) {
26280             Roo.log('Invalid Exif data: Invalid directory offset.');
26281             return;
26282         }
26283         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26284         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26285         if (dirEndOffset + 4 > dataView.byteLength) {
26286             Roo.log('Invalid Exif data: Invalid directory size.');
26287             return;
26288         }
26289         for (i = 0; i < tagsNumber; i += 1) {
26290             this.parseExifTag(
26291                 dataView,
26292                 tiffOffset,
26293                 dirOffset + 2 + 12 * i, // tag offset
26294                 littleEndian
26295             );
26296         }
26297         // Return the offset to the next directory:
26298         return dataView.getUint32(dirEndOffset, littleEndian);
26299     },
26300     
26301     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26302     {
26303         var tag = dataView.getUint16(offset, littleEndian);
26304         
26305         this.exif[tag] = this.getExifValue(
26306             dataView,
26307             tiffOffset,
26308             offset,
26309             dataView.getUint16(offset + 2, littleEndian), // tag type
26310             dataView.getUint32(offset + 4, littleEndian), // tag length
26311             littleEndian
26312         );
26313     },
26314     
26315     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26316     {
26317         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26318             tagSize,
26319             dataOffset,
26320             values,
26321             i,
26322             str,
26323             c;
26324     
26325         if (!tagType) {
26326             Roo.log('Invalid Exif data: Invalid tag type.');
26327             return;
26328         }
26329         
26330         tagSize = tagType.size * length;
26331         // Determine if the value is contained in the dataOffset bytes,
26332         // or if the value at the dataOffset is a pointer to the actual data:
26333         dataOffset = tagSize > 4 ?
26334                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26335         if (dataOffset + tagSize > dataView.byteLength) {
26336             Roo.log('Invalid Exif data: Invalid data offset.');
26337             return;
26338         }
26339         if (length === 1) {
26340             return tagType.getValue(dataView, dataOffset, littleEndian);
26341         }
26342         values = [];
26343         for (i = 0; i < length; i += 1) {
26344             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26345         }
26346         
26347         if (tagType.ascii) {
26348             str = '';
26349             // Concatenate the chars:
26350             for (i = 0; i < values.length; i += 1) {
26351                 c = values[i];
26352                 // Ignore the terminating NULL byte(s):
26353                 if (c === '\u0000') {
26354                     break;
26355                 }
26356                 str += c;
26357             }
26358             return str;
26359         }
26360         return values;
26361     }
26362     
26363 });
26364
26365 Roo.apply(Roo.bootstrap.UploadCropbox, {
26366     tags : {
26367         'Orientation': 0x0112
26368     },
26369     
26370     Orientation: {
26371             1: 0, //'top-left',
26372 //            2: 'top-right',
26373             3: 180, //'bottom-right',
26374 //            4: 'bottom-left',
26375 //            5: 'left-top',
26376             6: 90, //'right-top',
26377 //            7: 'right-bottom',
26378             8: 270 //'left-bottom'
26379     },
26380     
26381     exifTagTypes : {
26382         // byte, 8-bit unsigned int:
26383         1: {
26384             getValue: function (dataView, dataOffset) {
26385                 return dataView.getUint8(dataOffset);
26386             },
26387             size: 1
26388         },
26389         // ascii, 8-bit byte:
26390         2: {
26391             getValue: function (dataView, dataOffset) {
26392                 return String.fromCharCode(dataView.getUint8(dataOffset));
26393             },
26394             size: 1,
26395             ascii: true
26396         },
26397         // short, 16 bit int:
26398         3: {
26399             getValue: function (dataView, dataOffset, littleEndian) {
26400                 return dataView.getUint16(dataOffset, littleEndian);
26401             },
26402             size: 2
26403         },
26404         // long, 32 bit int:
26405         4: {
26406             getValue: function (dataView, dataOffset, littleEndian) {
26407                 return dataView.getUint32(dataOffset, littleEndian);
26408             },
26409             size: 4
26410         },
26411         // rational = two long values, first is numerator, second is denominator:
26412         5: {
26413             getValue: function (dataView, dataOffset, littleEndian) {
26414                 return dataView.getUint32(dataOffset, littleEndian) /
26415                     dataView.getUint32(dataOffset + 4, littleEndian);
26416             },
26417             size: 8
26418         },
26419         // slong, 32 bit signed int:
26420         9: {
26421             getValue: function (dataView, dataOffset, littleEndian) {
26422                 return dataView.getInt32(dataOffset, littleEndian);
26423             },
26424             size: 4
26425         },
26426         // srational, two slongs, first is numerator, second is denominator:
26427         10: {
26428             getValue: function (dataView, dataOffset, littleEndian) {
26429                 return dataView.getInt32(dataOffset, littleEndian) /
26430                     dataView.getInt32(dataOffset + 4, littleEndian);
26431             },
26432             size: 8
26433         }
26434     },
26435     
26436     footer : {
26437         STANDARD : [
26438             {
26439                 tag : 'div',
26440                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26441                 action : 'rotate-left',
26442                 cn : [
26443                     {
26444                         tag : 'button',
26445                         cls : 'btn btn-default',
26446                         html : '<i class="fa fa-undo"></i>'
26447                     }
26448                 ]
26449             },
26450             {
26451                 tag : 'div',
26452                 cls : 'btn-group roo-upload-cropbox-picture',
26453                 action : 'picture',
26454                 cn : [
26455                     {
26456                         tag : 'button',
26457                         cls : 'btn btn-default',
26458                         html : '<i class="fa fa-picture-o"></i>'
26459                     }
26460                 ]
26461             },
26462             {
26463                 tag : 'div',
26464                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26465                 action : 'rotate-right',
26466                 cn : [
26467                     {
26468                         tag : 'button',
26469                         cls : 'btn btn-default',
26470                         html : '<i class="fa fa-repeat"></i>'
26471                     }
26472                 ]
26473             }
26474         ],
26475         DOCUMENT : [
26476             {
26477                 tag : 'div',
26478                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26479                 action : 'rotate-left',
26480                 cn : [
26481                     {
26482                         tag : 'button',
26483                         cls : 'btn btn-default',
26484                         html : '<i class="fa fa-undo"></i>'
26485                     }
26486                 ]
26487             },
26488             {
26489                 tag : 'div',
26490                 cls : 'btn-group roo-upload-cropbox-download',
26491                 action : 'download',
26492                 cn : [
26493                     {
26494                         tag : 'button',
26495                         cls : 'btn btn-default',
26496                         html : '<i class="fa fa-download"></i>'
26497                     }
26498                 ]
26499             },
26500             {
26501                 tag : 'div',
26502                 cls : 'btn-group roo-upload-cropbox-crop',
26503                 action : 'crop',
26504                 cn : [
26505                     {
26506                         tag : 'button',
26507                         cls : 'btn btn-default',
26508                         html : '<i class="fa fa-crop"></i>'
26509                     }
26510                 ]
26511             },
26512             {
26513                 tag : 'div',
26514                 cls : 'btn-group roo-upload-cropbox-trash',
26515                 action : 'trash',
26516                 cn : [
26517                     {
26518                         tag : 'button',
26519                         cls : 'btn btn-default',
26520                         html : '<i class="fa fa-trash"></i>'
26521                     }
26522                 ]
26523             },
26524             {
26525                 tag : 'div',
26526                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26527                 action : 'rotate-right',
26528                 cn : [
26529                     {
26530                         tag : 'button',
26531                         cls : 'btn btn-default',
26532                         html : '<i class="fa fa-repeat"></i>'
26533                     }
26534                 ]
26535             }
26536         ],
26537         ROTATOR : [
26538             {
26539                 tag : 'div',
26540                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26541                 action : 'rotate-left',
26542                 cn : [
26543                     {
26544                         tag : 'button',
26545                         cls : 'btn btn-default',
26546                         html : '<i class="fa fa-undo"></i>'
26547                     }
26548                 ]
26549             },
26550             {
26551                 tag : 'div',
26552                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26553                 action : 'rotate-right',
26554                 cn : [
26555                     {
26556                         tag : 'button',
26557                         cls : 'btn btn-default',
26558                         html : '<i class="fa fa-repeat"></i>'
26559                     }
26560                 ]
26561             }
26562         ]
26563     }
26564 });
26565
26566 /*
26567 * Licence: LGPL
26568 */
26569
26570 /**
26571  * @class Roo.bootstrap.DocumentManager
26572  * @extends Roo.bootstrap.Component
26573  * Bootstrap DocumentManager class
26574  * @cfg {String} paramName default 'imageUpload'
26575  * @cfg {String} method default POST
26576  * @cfg {String} url action url
26577  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26578  * @cfg {Boolean} multiple multiple upload default true
26579  * @cfg {Number} thumbSize default 300
26580  * @cfg {String} fieldLabel
26581  * @cfg {Number} labelWidth default 4
26582  * @cfg {String} labelAlign (left|top) default left
26583  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26584  * 
26585  * @constructor
26586  * Create a new DocumentManager
26587  * @param {Object} config The config object
26588  */
26589
26590 Roo.bootstrap.DocumentManager = function(config){
26591     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26592     
26593     this.addEvents({
26594         /**
26595          * @event initial
26596          * Fire when initial the DocumentManager
26597          * @param {Roo.bootstrap.DocumentManager} this
26598          */
26599         "initial" : true,
26600         /**
26601          * @event inspect
26602          * inspect selected file
26603          * @param {Roo.bootstrap.DocumentManager} this
26604          * @param {File} file
26605          */
26606         "inspect" : true,
26607         /**
26608          * @event exception
26609          * Fire when xhr load exception
26610          * @param {Roo.bootstrap.DocumentManager} this
26611          * @param {XMLHttpRequest} xhr
26612          */
26613         "exception" : true,
26614         /**
26615          * @event prepare
26616          * prepare the form data
26617          * @param {Roo.bootstrap.DocumentManager} this
26618          * @param {Object} formData
26619          */
26620         "prepare" : true,
26621         /**
26622          * @event remove
26623          * Fire when remove the file
26624          * @param {Roo.bootstrap.DocumentManager} this
26625          * @param {Object} file
26626          */
26627         "remove" : true,
26628         /**
26629          * @event refresh
26630          * Fire after refresh the file
26631          * @param {Roo.bootstrap.DocumentManager} this
26632          */
26633         "refresh" : true,
26634         /**
26635          * @event click
26636          * Fire after click the image
26637          * @param {Roo.bootstrap.DocumentManager} this
26638          * @param {Object} file
26639          */
26640         "click" : true,
26641         /**
26642          * @event edit
26643          * Fire when upload a image and editable set to true
26644          * @param {Roo.bootstrap.DocumentManager} this
26645          * @param {Object} file
26646          */
26647         "edit" : true,
26648         /**
26649          * @event beforeselectfile
26650          * Fire before select file
26651          * @param {Roo.bootstrap.DocumentManager} this
26652          */
26653         "beforeselectfile" : true,
26654         /**
26655          * @event process
26656          * Fire before process file
26657          * @param {Roo.bootstrap.DocumentManager} this
26658          * @param {Object} file
26659          */
26660         "process" : true
26661         
26662     });
26663 };
26664
26665 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26666     
26667     boxes : 0,
26668     inputName : '',
26669     thumbSize : 300,
26670     multiple : true,
26671     files : [],
26672     method : 'POST',
26673     url : '',
26674     paramName : 'imageUpload',
26675     fieldLabel : '',
26676     labelWidth : 4,
26677     labelAlign : 'left',
26678     editable : true,
26679     delegates : [],
26680     
26681     
26682     xhr : false, 
26683     
26684     getAutoCreate : function()
26685     {   
26686         var managerWidget = {
26687             tag : 'div',
26688             cls : 'roo-document-manager',
26689             cn : [
26690                 {
26691                     tag : 'input',
26692                     cls : 'roo-document-manager-selector',
26693                     type : 'file'
26694                 },
26695                 {
26696                     tag : 'div',
26697                     cls : 'roo-document-manager-uploader',
26698                     cn : [
26699                         {
26700                             tag : 'div',
26701                             cls : 'roo-document-manager-upload-btn',
26702                             html : '<i class="fa fa-plus"></i>'
26703                         }
26704                     ]
26705                     
26706                 }
26707             ]
26708         };
26709         
26710         var content = [
26711             {
26712                 tag : 'div',
26713                 cls : 'column col-md-12',
26714                 cn : managerWidget
26715             }
26716         ];
26717         
26718         if(this.fieldLabel.length){
26719             
26720             content = [
26721                 {
26722                     tag : 'div',
26723                     cls : 'column col-md-12',
26724                     html : this.fieldLabel
26725                 },
26726                 {
26727                     tag : 'div',
26728                     cls : 'column col-md-12',
26729                     cn : managerWidget
26730                 }
26731             ];
26732
26733             if(this.labelAlign == 'left'){
26734                 content = [
26735                     {
26736                         tag : 'div',
26737                         cls : 'column col-md-' + this.labelWidth,
26738                         html : this.fieldLabel
26739                     },
26740                     {
26741                         tag : 'div',
26742                         cls : 'column col-md-' + (12 - this.labelWidth),
26743                         cn : managerWidget
26744                     }
26745                 ];
26746                 
26747             }
26748         }
26749         
26750         var cfg = {
26751             tag : 'div',
26752             cls : 'row clearfix',
26753             cn : content
26754         };
26755         
26756         return cfg;
26757         
26758     },
26759     
26760     initEvents : function()
26761     {
26762         this.managerEl = this.el.select('.roo-document-manager', true).first();
26763         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26764         
26765         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26766         this.selectorEl.hide();
26767         
26768         if(this.multiple){
26769             this.selectorEl.attr('multiple', 'multiple');
26770         }
26771         
26772         this.selectorEl.on('change', this.onFileSelected, this);
26773         
26774         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26775         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26776         
26777         this.uploader.on('click', this.onUploaderClick, this);
26778         
26779         this.renderProgressDialog();
26780         
26781         var _this = this;
26782         
26783         window.addEventListener("resize", function() { _this.refresh(); } );
26784         
26785         this.fireEvent('initial', this);
26786     },
26787     
26788     renderProgressDialog : function()
26789     {
26790         var _this = this;
26791         
26792         this.progressDialog = new Roo.bootstrap.Modal({
26793             cls : 'roo-document-manager-progress-dialog',
26794             allow_close : false,
26795             title : '',
26796             buttons : [
26797                 {
26798                     name  :'cancel',
26799                     weight : 'danger',
26800                     html : 'Cancel'
26801                 }
26802             ], 
26803             listeners : { 
26804                 btnclick : function() {
26805                     _this.uploadCancel();
26806                     this.hide();
26807                 }
26808             }
26809         });
26810          
26811         this.progressDialog.render(Roo.get(document.body));
26812          
26813         this.progress = new Roo.bootstrap.Progress({
26814             cls : 'roo-document-manager-progress',
26815             active : true,
26816             striped : true
26817         });
26818         
26819         this.progress.render(this.progressDialog.getChildContainer());
26820         
26821         this.progressBar = new Roo.bootstrap.ProgressBar({
26822             cls : 'roo-document-manager-progress-bar',
26823             aria_valuenow : 0,
26824             aria_valuemin : 0,
26825             aria_valuemax : 12,
26826             panel : 'success'
26827         });
26828         
26829         this.progressBar.render(this.progress.getChildContainer());
26830     },
26831     
26832     onUploaderClick : function(e)
26833     {
26834         e.preventDefault();
26835      
26836         if(this.fireEvent('beforeselectfile', this) != false){
26837             this.selectorEl.dom.click();
26838         }
26839         
26840     },
26841     
26842     onFileSelected : function(e)
26843     {
26844         e.preventDefault();
26845         
26846         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26847             return;
26848         }
26849         
26850         Roo.each(this.selectorEl.dom.files, function(file){
26851             if(this.fireEvent('inspect', this, file) != false){
26852                 this.files.push(file);
26853             }
26854         }, this);
26855         
26856         this.queue();
26857         
26858     },
26859     
26860     queue : function()
26861     {
26862         this.selectorEl.dom.value = '';
26863         
26864         if(!this.files.length){
26865             return;
26866         }
26867         
26868         if(this.boxes > 0 && this.files.length > this.boxes){
26869             this.files = this.files.slice(0, this.boxes);
26870         }
26871         
26872         this.uploader.show();
26873         
26874         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26875             this.uploader.hide();
26876         }
26877         
26878         var _this = this;
26879         
26880         var files = [];
26881         
26882         var docs = [];
26883         
26884         Roo.each(this.files, function(file){
26885             
26886             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26887                 var f = this.renderPreview(file);
26888                 files.push(f);
26889                 return;
26890             }
26891             
26892             if(file.type.indexOf('image') != -1){
26893                 this.delegates.push(
26894                     (function(){
26895                         _this.process(file);
26896                     }).createDelegate(this)
26897                 );
26898         
26899                 return;
26900             }
26901             
26902             docs.push(
26903                 (function(){
26904                     _this.process(file);
26905                 }).createDelegate(this)
26906             );
26907             
26908         }, this);
26909         
26910         this.files = files;
26911         
26912         this.delegates = this.delegates.concat(docs);
26913         
26914         if(!this.delegates.length){
26915             this.refresh();
26916             return;
26917         }
26918         
26919         this.progressBar.aria_valuemax = this.delegates.length;
26920         
26921         this.arrange();
26922         
26923         return;
26924     },
26925     
26926     arrange : function()
26927     {
26928         if(!this.delegates.length){
26929             this.progressDialog.hide();
26930             this.refresh();
26931             return;
26932         }
26933         
26934         var delegate = this.delegates.shift();
26935         
26936         this.progressDialog.show();
26937         
26938         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26939         
26940         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26941         
26942         delegate();
26943     },
26944     
26945     refresh : function()
26946     {
26947         this.uploader.show();
26948         
26949         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26950             this.uploader.hide();
26951         }
26952         
26953         Roo.isTouch ? this.closable(false) : this.closable(true);
26954         
26955         this.fireEvent('refresh', this);
26956     },
26957     
26958     onRemove : function(e, el, o)
26959     {
26960         e.preventDefault();
26961         
26962         this.fireEvent('remove', this, o);
26963         
26964     },
26965     
26966     remove : function(o)
26967     {
26968         var files = [];
26969         
26970         Roo.each(this.files, function(file){
26971             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26972                 files.push(file);
26973                 return;
26974             }
26975
26976             o.target.remove();
26977
26978         }, this);
26979         
26980         this.files = files;
26981         
26982         this.refresh();
26983     },
26984     
26985     clear : function()
26986     {
26987         Roo.each(this.files, function(file){
26988             if(!file.target){
26989                 return;
26990             }
26991             
26992             file.target.remove();
26993
26994         }, this);
26995         
26996         this.files = [];
26997         
26998         this.refresh();
26999     },
27000     
27001     onClick : function(e, el, o)
27002     {
27003         e.preventDefault();
27004         
27005         this.fireEvent('click', this, o);
27006         
27007     },
27008     
27009     closable : function(closable)
27010     {
27011         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27012             
27013             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27014             
27015             if(closable){
27016                 el.show();
27017                 return;
27018             }
27019             
27020             el.hide();
27021             
27022         }, this);
27023     },
27024     
27025     xhrOnLoad : function(xhr)
27026     {
27027         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27028             el.remove();
27029         }, this);
27030         
27031         if (xhr.readyState !== 4) {
27032             this.arrange();
27033             this.fireEvent('exception', this, xhr);
27034             return;
27035         }
27036
27037         var response = Roo.decode(xhr.responseText);
27038         
27039         if(!response.success){
27040             this.arrange();
27041             this.fireEvent('exception', this, xhr);
27042             return;
27043         }
27044         
27045         var file = this.renderPreview(response.data);
27046         
27047         this.files.push(file);
27048         
27049         this.arrange();
27050         
27051     },
27052     
27053     xhrOnError : function(xhr)
27054     {
27055         Roo.log('xhr on error');
27056         
27057         var response = Roo.decode(xhr.responseText);
27058           
27059         Roo.log(response);
27060         
27061         this.arrange();
27062     },
27063     
27064     process : function(file)
27065     {
27066         if(this.fireEvent('process', this, file) !== false){
27067             if(this.editable && file.type.indexOf('image') != -1){
27068                 this.fireEvent('edit', this, file);
27069                 return;
27070             }
27071
27072             this.uploadStart(file, false);
27073
27074             return;
27075         }
27076         
27077     },
27078     
27079     uploadStart : function(file, crop)
27080     {
27081         this.xhr = new XMLHttpRequest();
27082         
27083         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27084             this.arrange();
27085             return;
27086         }
27087         
27088         file.xhr = this.xhr;
27089             
27090         this.managerEl.createChild({
27091             tag : 'div',
27092             cls : 'roo-document-manager-loading',
27093             cn : [
27094                 {
27095                     tag : 'div',
27096                     tooltip : file.name,
27097                     cls : 'roo-document-manager-thumb',
27098                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27099                 }
27100             ]
27101
27102         });
27103
27104         this.xhr.open(this.method, this.url, true);
27105         
27106         var headers = {
27107             "Accept": "application/json",
27108             "Cache-Control": "no-cache",
27109             "X-Requested-With": "XMLHttpRequest"
27110         };
27111         
27112         for (var headerName in headers) {
27113             var headerValue = headers[headerName];
27114             if (headerValue) {
27115                 this.xhr.setRequestHeader(headerName, headerValue);
27116             }
27117         }
27118         
27119         var _this = this;
27120         
27121         this.xhr.onload = function()
27122         {
27123             _this.xhrOnLoad(_this.xhr);
27124         }
27125         
27126         this.xhr.onerror = function()
27127         {
27128             _this.xhrOnError(_this.xhr);
27129         }
27130         
27131         var formData = new FormData();
27132
27133         formData.append('returnHTML', 'NO');
27134         
27135         if(crop){
27136             formData.append('crop', crop);
27137         }
27138         
27139         formData.append(this.paramName, file, file.name);
27140         
27141         if(this.fireEvent('prepare', this, formData) != false){
27142             this.xhr.send(formData);
27143         };
27144     },
27145     
27146     uploadCancel : function()
27147     {
27148         if (this.xhr) {
27149             this.xhr.abort();
27150         }
27151         
27152         
27153         this.delegates = [];
27154         
27155         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27156             el.remove();
27157         }, this);
27158         
27159         this.arrange();
27160     },
27161     
27162     renderPreview : function(file)
27163     {
27164         if(typeof(file.target) != 'undefined' && file.target){
27165             return file;
27166         }
27167         
27168         var previewEl = this.managerEl.createChild({
27169             tag : 'div',
27170             cls : 'roo-document-manager-preview',
27171             cn : [
27172                 {
27173                     tag : 'div',
27174                     tooltip : file.filename,
27175                     cls : 'roo-document-manager-thumb',
27176                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27177                 },
27178                 {
27179                     tag : 'button',
27180                     cls : 'close',
27181                     html : '<i class="fa fa-times-circle"></i>'
27182                 }
27183             ]
27184         });
27185
27186         var close = previewEl.select('button.close', true).first();
27187
27188         close.on('click', this.onRemove, this, file);
27189
27190         file.target = previewEl;
27191
27192         var image = previewEl.select('img', true).first();
27193         
27194         var _this = this;
27195         
27196         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27197         
27198         image.on('click', this.onClick, this, file);
27199         
27200         return file;
27201         
27202     },
27203     
27204     onPreviewLoad : function(file, image)
27205     {
27206         if(typeof(file.target) == 'undefined' || !file.target){
27207             return;
27208         }
27209         
27210         var width = image.dom.naturalWidth || image.dom.width;
27211         var height = image.dom.naturalHeight || image.dom.height;
27212         
27213         if(width > height){
27214             file.target.addClass('wide');
27215             return;
27216         }
27217         
27218         file.target.addClass('tall');
27219         return;
27220         
27221     },
27222     
27223     uploadFromSource : function(file, crop)
27224     {
27225         this.xhr = new XMLHttpRequest();
27226         
27227         this.managerEl.createChild({
27228             tag : 'div',
27229             cls : 'roo-document-manager-loading',
27230             cn : [
27231                 {
27232                     tag : 'div',
27233                     tooltip : file.name,
27234                     cls : 'roo-document-manager-thumb',
27235                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27236                 }
27237             ]
27238
27239         });
27240
27241         this.xhr.open(this.method, this.url, true);
27242         
27243         var headers = {
27244             "Accept": "application/json",
27245             "Cache-Control": "no-cache",
27246             "X-Requested-With": "XMLHttpRequest"
27247         };
27248         
27249         for (var headerName in headers) {
27250             var headerValue = headers[headerName];
27251             if (headerValue) {
27252                 this.xhr.setRequestHeader(headerName, headerValue);
27253             }
27254         }
27255         
27256         var _this = this;
27257         
27258         this.xhr.onload = function()
27259         {
27260             _this.xhrOnLoad(_this.xhr);
27261         }
27262         
27263         this.xhr.onerror = function()
27264         {
27265             _this.xhrOnError(_this.xhr);
27266         }
27267         
27268         var formData = new FormData();
27269
27270         formData.append('returnHTML', 'NO');
27271         
27272         formData.append('crop', crop);
27273         
27274         if(typeof(file.filename) != 'undefined'){
27275             formData.append('filename', file.filename);
27276         }
27277         
27278         if(typeof(file.mimetype) != 'undefined'){
27279             formData.append('mimetype', file.mimetype);
27280         }
27281         
27282         if(this.fireEvent('prepare', this, formData) != false){
27283             this.xhr.send(formData);
27284         };
27285     }
27286 });
27287
27288 /*
27289 * Licence: LGPL
27290 */
27291
27292 /**
27293  * @class Roo.bootstrap.DocumentViewer
27294  * @extends Roo.bootstrap.Component
27295  * Bootstrap DocumentViewer class
27296  * 
27297  * @constructor
27298  * Create a new DocumentViewer
27299  * @param {Object} config The config object
27300  */
27301
27302 Roo.bootstrap.DocumentViewer = function(config){
27303     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27304     
27305     this.addEvents({
27306         /**
27307          * @event initial
27308          * Fire after initEvent
27309          * @param {Roo.bootstrap.DocumentViewer} this
27310          */
27311         "initial" : true,
27312         /**
27313          * @event click
27314          * Fire after click
27315          * @param {Roo.bootstrap.DocumentViewer} this
27316          */
27317         "click" : true,
27318         /**
27319          * @event trash
27320          * Fire after trash button
27321          * @param {Roo.bootstrap.DocumentViewer} this
27322          */
27323         "trash" : true
27324         
27325     });
27326 };
27327
27328 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27329     
27330     getAutoCreate : function()
27331     {
27332         var cfg = {
27333             tag : 'div',
27334             cls : 'roo-document-viewer',
27335             cn : [
27336                 {
27337                     tag : 'div',
27338                     cls : 'roo-document-viewer-body',
27339                     cn : [
27340                         {
27341                             tag : 'div',
27342                             cls : 'roo-document-viewer-thumb',
27343                             cn : [
27344                                 {
27345                                     tag : 'img',
27346                                     cls : 'roo-document-viewer-image'
27347                                 }
27348                             ]
27349                         }
27350                     ]
27351                 },
27352                 {
27353                     tag : 'div',
27354                     cls : 'roo-document-viewer-footer',
27355                     cn : {
27356                         tag : 'div',
27357                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27358                         cn : [
27359                             {
27360                                 tag : 'div',
27361                                 cls : 'btn-group',
27362                                 cn : [
27363                                     {
27364                                         tag : 'button',
27365                                         cls : 'btn btn-default roo-document-viewer-trash',
27366                                         html : '<i class="fa fa-trash"></i>'
27367                                     }
27368                                 ]
27369                             }
27370                         ]
27371                     }
27372                 }
27373             ]
27374         };
27375         
27376         return cfg;
27377     },
27378     
27379     initEvents : function()
27380     {
27381         
27382         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27383         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27384         
27385         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27386         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27387         
27388         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27389         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27390         
27391         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27392         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27393         
27394         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27395         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27396         
27397         this.bodyEl.on('click', this.onClick, this);
27398         
27399         this.trashBtn.on('click', this.onTrash, this);
27400         
27401     },
27402     
27403     initial : function()
27404     {
27405 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27406         
27407         
27408         this.fireEvent('initial', this);
27409         
27410     },
27411     
27412     onClick : function(e)
27413     {
27414         e.preventDefault();
27415         
27416         this.fireEvent('click', this);
27417     },
27418     
27419     onTrash : function(e)
27420     {
27421         e.preventDefault();
27422         
27423         this.fireEvent('trash', this);
27424     }
27425     
27426 });
27427 /*
27428  * - LGPL
27429  *
27430  * nav progress bar
27431  * 
27432  */
27433
27434 /**
27435  * @class Roo.bootstrap.NavProgressBar
27436  * @extends Roo.bootstrap.Component
27437  * Bootstrap NavProgressBar class
27438  * 
27439  * @constructor
27440  * Create a new nav progress bar
27441  * @param {Object} config The config object
27442  */
27443
27444 Roo.bootstrap.NavProgressBar = function(config){
27445     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27446
27447     this.bullets = this.bullets || [];
27448    
27449 //    Roo.bootstrap.NavProgressBar.register(this);
27450      this.addEvents({
27451         /**
27452              * @event changed
27453              * Fires when the active item changes
27454              * @param {Roo.bootstrap.NavProgressBar} this
27455              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27456              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27457          */
27458         'changed': true
27459      });
27460     
27461 };
27462
27463 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27464     
27465     bullets : [],
27466     barItems : [],
27467     
27468     getAutoCreate : function()
27469     {
27470         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27471         
27472         cfg = {
27473             tag : 'div',
27474             cls : 'roo-navigation-bar-group',
27475             cn : [
27476                 {
27477                     tag : 'div',
27478                     cls : 'roo-navigation-top-bar'
27479                 },
27480                 {
27481                     tag : 'div',
27482                     cls : 'roo-navigation-bullets-bar',
27483                     cn : [
27484                         {
27485                             tag : 'ul',
27486                             cls : 'roo-navigation-bar'
27487                         }
27488                     ]
27489                 },
27490                 
27491                 {
27492                     tag : 'div',
27493                     cls : 'roo-navigation-bottom-bar'
27494                 }
27495             ]
27496             
27497         };
27498         
27499         return cfg;
27500         
27501     },
27502     
27503     initEvents: function() 
27504     {
27505         
27506     },
27507     
27508     onRender : function(ct, position) 
27509     {
27510         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27511         
27512         if(this.bullets.length){
27513             Roo.each(this.bullets, function(b){
27514                this.addItem(b);
27515             }, this);
27516         }
27517         
27518         this.format();
27519         
27520     },
27521     
27522     addItem : function(cfg)
27523     {
27524         var item = new Roo.bootstrap.NavProgressItem(cfg);
27525         
27526         item.parentId = this.id;
27527         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27528         
27529         if(cfg.html){
27530             var top = new Roo.bootstrap.Element({
27531                 tag : 'div',
27532                 cls : 'roo-navigation-bar-text'
27533             });
27534             
27535             var bottom = new Roo.bootstrap.Element({
27536                 tag : 'div',
27537                 cls : 'roo-navigation-bar-text'
27538             });
27539             
27540             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27541             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27542             
27543             var topText = new Roo.bootstrap.Element({
27544                 tag : 'span',
27545                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27546             });
27547             
27548             var bottomText = new Roo.bootstrap.Element({
27549                 tag : 'span',
27550                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27551             });
27552             
27553             topText.onRender(top.el, null);
27554             bottomText.onRender(bottom.el, null);
27555             
27556             item.topEl = top;
27557             item.bottomEl = bottom;
27558         }
27559         
27560         this.barItems.push(item);
27561         
27562         return item;
27563     },
27564     
27565     getActive : function()
27566     {
27567         var active = false;
27568         
27569         Roo.each(this.barItems, function(v){
27570             
27571             if (!v.isActive()) {
27572                 return;
27573             }
27574             
27575             active = v;
27576             return false;
27577             
27578         });
27579         
27580         return active;
27581     },
27582     
27583     setActiveItem : function(item)
27584     {
27585         var prev = false;
27586         
27587         Roo.each(this.barItems, function(v){
27588             if (v.rid == item.rid) {
27589                 return ;
27590             }
27591             
27592             if (v.isActive()) {
27593                 v.setActive(false);
27594                 prev = v;
27595             }
27596         });
27597
27598         item.setActive(true);
27599         
27600         this.fireEvent('changed', this, item, prev);
27601     },
27602     
27603     getBarItem: function(rid)
27604     {
27605         var ret = false;
27606         
27607         Roo.each(this.barItems, function(e) {
27608             if (e.rid != rid) {
27609                 return;
27610             }
27611             
27612             ret =  e;
27613             return false;
27614         });
27615         
27616         return ret;
27617     },
27618     
27619     indexOfItem : function(item)
27620     {
27621         var index = false;
27622         
27623         Roo.each(this.barItems, function(v, i){
27624             
27625             if (v.rid != item.rid) {
27626                 return;
27627             }
27628             
27629             index = i;
27630             return false
27631         });
27632         
27633         return index;
27634     },
27635     
27636     setActiveNext : function()
27637     {
27638         var i = this.indexOfItem(this.getActive());
27639         
27640         if (i > this.barItems.length) {
27641             return;
27642         }
27643         
27644         this.setActiveItem(this.barItems[i+1]);
27645     },
27646     
27647     setActivePrev : function()
27648     {
27649         var i = this.indexOfItem(this.getActive());
27650         
27651         if (i  < 1) {
27652             return;
27653         }
27654         
27655         this.setActiveItem(this.barItems[i-1]);
27656     },
27657     
27658     format : function()
27659     {
27660         if(!this.barItems.length){
27661             return;
27662         }
27663      
27664         var width = 100 / this.barItems.length;
27665         
27666         Roo.each(this.barItems, function(i){
27667             i.el.setStyle('width', width + '%');
27668             i.topEl.el.setStyle('width', width + '%');
27669             i.bottomEl.el.setStyle('width', width + '%');
27670         }, this);
27671         
27672     }
27673     
27674 });
27675 /*
27676  * - LGPL
27677  *
27678  * Nav Progress Item
27679  * 
27680  */
27681
27682 /**
27683  * @class Roo.bootstrap.NavProgressItem
27684  * @extends Roo.bootstrap.Component
27685  * Bootstrap NavProgressItem class
27686  * @cfg {String} rid the reference id
27687  * @cfg {Boolean} active (true|false) Is item active default false
27688  * @cfg {Boolean} disabled (true|false) Is item active default false
27689  * @cfg {String} html
27690  * @cfg {String} position (top|bottom) text position default bottom
27691  * @cfg {String} icon show icon instead of number
27692  * 
27693  * @constructor
27694  * Create a new NavProgressItem
27695  * @param {Object} config The config object
27696  */
27697 Roo.bootstrap.NavProgressItem = function(config){
27698     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27699     this.addEvents({
27700         // raw events
27701         /**
27702          * @event click
27703          * The raw click event for the entire grid.
27704          * @param {Roo.bootstrap.NavProgressItem} this
27705          * @param {Roo.EventObject} e
27706          */
27707         "click" : true
27708     });
27709    
27710 };
27711
27712 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27713     
27714     rid : '',
27715     active : false,
27716     disabled : false,
27717     html : '',
27718     position : 'bottom',
27719     icon : false,
27720     
27721     getAutoCreate : function()
27722     {
27723         var iconCls = 'roo-navigation-bar-item-icon';
27724         
27725         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27726         
27727         var cfg = {
27728             tag: 'li',
27729             cls: 'roo-navigation-bar-item',
27730             cn : [
27731                 {
27732                     tag : 'i',
27733                     cls : iconCls
27734                 }
27735             ]
27736         };
27737         
27738         if(this.active){
27739             cfg.cls += ' active';
27740         }
27741         if(this.disabled){
27742             cfg.cls += ' disabled';
27743         }
27744         
27745         return cfg;
27746     },
27747     
27748     disable : function()
27749     {
27750         this.setDisabled(true);
27751     },
27752     
27753     enable : function()
27754     {
27755         this.setDisabled(false);
27756     },
27757     
27758     initEvents: function() 
27759     {
27760         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27761         
27762         this.iconEl.on('click', this.onClick, this);
27763     },
27764     
27765     onClick : function(e)
27766     {
27767         e.preventDefault();
27768         
27769         if(this.disabled){
27770             return;
27771         }
27772         
27773         if(this.fireEvent('click', this, e) === false){
27774             return;
27775         };
27776         
27777         this.parent().setActiveItem(this);
27778     },
27779     
27780     isActive: function () 
27781     {
27782         return this.active;
27783     },
27784     
27785     setActive : function(state)
27786     {
27787         if(this.active == state){
27788             return;
27789         }
27790         
27791         this.active = state;
27792         
27793         if (state) {
27794             this.el.addClass('active');
27795             return;
27796         }
27797         
27798         this.el.removeClass('active');
27799         
27800         return;
27801     },
27802     
27803     setDisabled : function(state)
27804     {
27805         if(this.disabled == state){
27806             return;
27807         }
27808         
27809         this.disabled = state;
27810         
27811         if (state) {
27812             this.el.addClass('disabled');
27813             return;
27814         }
27815         
27816         this.el.removeClass('disabled');
27817     },
27818     
27819     tooltipEl : function()
27820     {
27821         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27822     }
27823 });
27824  
27825
27826  /*
27827  * - LGPL
27828  *
27829  * FieldLabel
27830  * 
27831  */
27832
27833 /**
27834  * @class Roo.bootstrap.FieldLabel
27835  * @extends Roo.bootstrap.Component
27836  * Bootstrap FieldLabel class
27837  * @cfg {String} html contents of the element
27838  * @cfg {String} tag tag of the element default label
27839  * @cfg {String} cls class of the element
27840  * @cfg {String} target label target 
27841  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27842  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27843  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27844  * @cfg {String} iconTooltip default "This field is required"
27845  * 
27846  * @constructor
27847  * Create a new FieldLabel
27848  * @param {Object} config The config object
27849  */
27850
27851 Roo.bootstrap.FieldLabel = function(config){
27852     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27853     
27854     this.addEvents({
27855             /**
27856              * @event invalid
27857              * Fires after the field has been marked as invalid.
27858              * @param {Roo.form.FieldLabel} this
27859              * @param {String} msg The validation message
27860              */
27861             invalid : true,
27862             /**
27863              * @event valid
27864              * Fires after the field has been validated with no errors.
27865              * @param {Roo.form.FieldLabel} this
27866              */
27867             valid : true
27868         });
27869 };
27870
27871 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27872     
27873     tag: 'label',
27874     cls: '',
27875     html: '',
27876     target: '',
27877     allowBlank : true,
27878     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27879     validClass : 'text-success fa fa-lg fa-check',
27880     iconTooltip : 'This field is required',
27881     
27882     getAutoCreate : function(){
27883         
27884         var cfg = {
27885             tag : this.tag,
27886             cls : 'roo-bootstrap-field-label ' + this.cls,
27887             for : this.target,
27888             cn : [
27889                 {
27890                     tag : 'i',
27891                     cls : '',
27892                     tooltip : this.iconTooltip
27893                 },
27894                 {
27895                     tag : 'span',
27896                     html : this.html
27897                 }
27898             ] 
27899         };
27900         
27901         return cfg;
27902     },
27903     
27904     initEvents: function() 
27905     {
27906         Roo.bootstrap.Element.superclass.initEvents.call(this);
27907         
27908         this.iconEl = this.el.select('i', true).first();
27909         
27910         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27911         
27912         Roo.bootstrap.FieldLabel.register(this);
27913     },
27914     
27915     /**
27916      * Mark this field as valid
27917      */
27918     markValid : function()
27919     {
27920         this.iconEl.show();
27921         
27922         this.iconEl.removeClass(this.invalidClass);
27923         
27924         this.iconEl.addClass(this.validClass);
27925         
27926         this.fireEvent('valid', this);
27927     },
27928     
27929     /**
27930      * Mark this field as invalid
27931      * @param {String} msg The validation message
27932      */
27933     markInvalid : function(msg)
27934     {
27935         this.iconEl.show();
27936         
27937         this.iconEl.removeClass(this.validClass);
27938         
27939         this.iconEl.addClass(this.invalidClass);
27940         
27941         this.fireEvent('invalid', this, msg);
27942     }
27943     
27944    
27945 });
27946
27947 Roo.apply(Roo.bootstrap.FieldLabel, {
27948     
27949     groups: {},
27950     
27951      /**
27952     * register a FieldLabel Group
27953     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27954     */
27955     register : function(label)
27956     {
27957         if(this.groups.hasOwnProperty(label.target)){
27958             return;
27959         }
27960      
27961         this.groups[label.target] = label;
27962         
27963     },
27964     /**
27965     * fetch a FieldLabel Group based on the target
27966     * @param {string} target
27967     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27968     */
27969     get: function(target) {
27970         if (typeof(this.groups[target]) == 'undefined') {
27971             return false;
27972         }
27973         
27974         return this.groups[target] ;
27975     }
27976 });
27977
27978  
27979
27980  /*
27981  * - LGPL
27982  *
27983  * page DateSplitField.
27984  * 
27985  */
27986
27987
27988 /**
27989  * @class Roo.bootstrap.DateSplitField
27990  * @extends Roo.bootstrap.Component
27991  * Bootstrap DateSplitField class
27992  * @cfg {string} fieldLabel - the label associated
27993  * @cfg {Number} labelWidth set the width of label (0-12)
27994  * @cfg {String} labelAlign (top|left)
27995  * @cfg {Boolean} dayAllowBlank (true|false) default false
27996  * @cfg {Boolean} monthAllowBlank (true|false) default false
27997  * @cfg {Boolean} yearAllowBlank (true|false) default false
27998  * @cfg {string} dayPlaceholder 
27999  * @cfg {string} monthPlaceholder
28000  * @cfg {string} yearPlaceholder
28001  * @cfg {string} dayFormat default 'd'
28002  * @cfg {string} monthFormat default 'm'
28003  * @cfg {string} yearFormat default 'Y'
28004
28005  *     
28006  * @constructor
28007  * Create a new DateSplitField
28008  * @param {Object} config The config object
28009  */
28010
28011 Roo.bootstrap.DateSplitField = function(config){
28012     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28013     
28014     this.addEvents({
28015         // raw events
28016          /**
28017          * @event years
28018          * getting the data of years
28019          * @param {Roo.bootstrap.DateSplitField} this
28020          * @param {Object} years
28021          */
28022         "years" : true,
28023         /**
28024          * @event days
28025          * getting the data of days
28026          * @param {Roo.bootstrap.DateSplitField} this
28027          * @param {Object} days
28028          */
28029         "days" : true,
28030         /**
28031          * @event invalid
28032          * Fires after the field has been marked as invalid.
28033          * @param {Roo.form.Field} this
28034          * @param {String} msg The validation message
28035          */
28036         invalid : true,
28037        /**
28038          * @event valid
28039          * Fires after the field has been validated with no errors.
28040          * @param {Roo.form.Field} this
28041          */
28042         valid : true
28043     });
28044 };
28045
28046 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28047     
28048     fieldLabel : '',
28049     labelAlign : 'top',
28050     labelWidth : 3,
28051     dayAllowBlank : false,
28052     monthAllowBlank : false,
28053     yearAllowBlank : false,
28054     dayPlaceholder : '',
28055     monthPlaceholder : '',
28056     yearPlaceholder : '',
28057     dayFormat : 'd',
28058     monthFormat : 'm',
28059     yearFormat : 'Y',
28060     isFormField : true,
28061     
28062     getAutoCreate : function()
28063     {
28064         var cfg = {
28065             tag : 'div',
28066             cls : 'row roo-date-split-field-group',
28067             cn : [
28068                 {
28069                     tag : 'input',
28070                     type : 'hidden',
28071                     cls : 'form-hidden-field roo-date-split-field-group-value',
28072                     name : this.name
28073                 }
28074             ]
28075         };
28076         
28077         if(this.fieldLabel){
28078             cfg.cn.push({
28079                 tag : 'div',
28080                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28081                 cn : [
28082                     {
28083                         tag : 'label',
28084                         html : this.fieldLabel
28085                     }
28086                 ]
28087             });
28088         }
28089         
28090         Roo.each(['day', 'month', 'year'], function(t){
28091             cfg.cn.push({
28092                 tag : 'div',
28093                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28094             });
28095         }, this);
28096         
28097         return cfg;
28098     },
28099     
28100     inputEl: function ()
28101     {
28102         return this.el.select('.roo-date-split-field-group-value', true).first();
28103     },
28104     
28105     onRender : function(ct, position) 
28106     {
28107         var _this = this;
28108         
28109         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28110         
28111         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28112         
28113         this.dayField = new Roo.bootstrap.ComboBox({
28114             allowBlank : this.dayAllowBlank,
28115             alwaysQuery : true,
28116             displayField : 'value',
28117             editable : false,
28118             fieldLabel : '',
28119             forceSelection : true,
28120             mode : 'local',
28121             placeholder : this.dayPlaceholder,
28122             selectOnFocus : true,
28123             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28124             triggerAction : 'all',
28125             typeAhead : true,
28126             valueField : 'value',
28127             store : new Roo.data.SimpleStore({
28128                 data : (function() {    
28129                     var days = [];
28130                     _this.fireEvent('days', _this, days);
28131                     return days;
28132                 })(),
28133                 fields : [ 'value' ]
28134             }),
28135             listeners : {
28136                 select : function (_self, record, index)
28137                 {
28138                     _this.setValue(_this.getValue());
28139                 }
28140             }
28141         });
28142
28143         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28144         
28145         this.monthField = new Roo.bootstrap.MonthField({
28146             after : '<i class=\"fa fa-calendar\"></i>',
28147             allowBlank : this.monthAllowBlank,
28148             placeholder : this.monthPlaceholder,
28149             readOnly : true,
28150             listeners : {
28151                 render : function (_self)
28152                 {
28153                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28154                         e.preventDefault();
28155                         _self.focus();
28156                     });
28157                 },
28158                 select : function (_self, oldvalue, newvalue)
28159                 {
28160                     _this.setValue(_this.getValue());
28161                 }
28162             }
28163         });
28164         
28165         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28166         
28167         this.yearField = new Roo.bootstrap.ComboBox({
28168             allowBlank : this.yearAllowBlank,
28169             alwaysQuery : true,
28170             displayField : 'value',
28171             editable : false,
28172             fieldLabel : '',
28173             forceSelection : true,
28174             mode : 'local',
28175             placeholder : this.yearPlaceholder,
28176             selectOnFocus : true,
28177             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28178             triggerAction : 'all',
28179             typeAhead : true,
28180             valueField : 'value',
28181             store : new Roo.data.SimpleStore({
28182                 data : (function() {
28183                     var years = [];
28184                     _this.fireEvent('years', _this, years);
28185                     return years;
28186                 })(),
28187                 fields : [ 'value' ]
28188             }),
28189             listeners : {
28190                 select : function (_self, record, index)
28191                 {
28192                     _this.setValue(_this.getValue());
28193                 }
28194             }
28195         });
28196
28197         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28198     },
28199     
28200     setValue : function(v, format)
28201     {
28202         this.inputEl.dom.value = v;
28203         
28204         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28205         
28206         var d = Date.parseDate(v, f);
28207         
28208         if(!d){
28209             this.validate();
28210             return;
28211         }
28212         
28213         this.setDay(d.format(this.dayFormat));
28214         this.setMonth(d.format(this.monthFormat));
28215         this.setYear(d.format(this.yearFormat));
28216         
28217         this.validate();
28218         
28219         return;
28220     },
28221     
28222     setDay : function(v)
28223     {
28224         this.dayField.setValue(v);
28225         this.inputEl.dom.value = this.getValue();
28226         this.validate();
28227         return;
28228     },
28229     
28230     setMonth : function(v)
28231     {
28232         this.monthField.setValue(v, true);
28233         this.inputEl.dom.value = this.getValue();
28234         this.validate();
28235         return;
28236     },
28237     
28238     setYear : function(v)
28239     {
28240         this.yearField.setValue(v);
28241         this.inputEl.dom.value = this.getValue();
28242         this.validate();
28243         return;
28244     },
28245     
28246     getDay : function()
28247     {
28248         return this.dayField.getValue();
28249     },
28250     
28251     getMonth : function()
28252     {
28253         return this.monthField.getValue();
28254     },
28255     
28256     getYear : function()
28257     {
28258         return this.yearField.getValue();
28259     },
28260     
28261     getValue : function()
28262     {
28263         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28264         
28265         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28266         
28267         return date;
28268     },
28269     
28270     reset : function()
28271     {
28272         this.setDay('');
28273         this.setMonth('');
28274         this.setYear('');
28275         this.inputEl.dom.value = '';
28276         this.validate();
28277         return;
28278     },
28279     
28280     validate : function()
28281     {
28282         var d = this.dayField.validate();
28283         var m = this.monthField.validate();
28284         var y = this.yearField.validate();
28285         
28286         var valid = true;
28287         
28288         if(
28289                 (!this.dayAllowBlank && !d) ||
28290                 (!this.monthAllowBlank && !m) ||
28291                 (!this.yearAllowBlank && !y)
28292         ){
28293             valid = false;
28294         }
28295         
28296         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28297             return valid;
28298         }
28299         
28300         if(valid){
28301             this.markValid();
28302             return valid;
28303         }
28304         
28305         this.markInvalid();
28306         
28307         return valid;
28308     },
28309     
28310     markValid : function()
28311     {
28312         
28313         var label = this.el.select('label', true).first();
28314         var icon = this.el.select('i.fa-star', true).first();
28315
28316         if(label && icon){
28317             icon.remove();
28318         }
28319         
28320         this.fireEvent('valid', this);
28321     },
28322     
28323      /**
28324      * Mark this field as invalid
28325      * @param {String} msg The validation message
28326      */
28327     markInvalid : function(msg)
28328     {
28329         
28330         var label = this.el.select('label', true).first();
28331         var icon = this.el.select('i.fa-star', true).first();
28332
28333         if(label && !icon){
28334             this.el.select('.roo-date-split-field-label', true).createChild({
28335                 tag : 'i',
28336                 cls : 'text-danger fa fa-lg fa-star',
28337                 tooltip : 'This field is required',
28338                 style : 'margin-right:5px;'
28339             }, label, true);
28340         }
28341         
28342         this.fireEvent('invalid', this, msg);
28343     },
28344     
28345     clearInvalid : function()
28346     {
28347         var label = this.el.select('label', true).first();
28348         var icon = this.el.select('i.fa-star', true).first();
28349
28350         if(label && icon){
28351             icon.remove();
28352         }
28353         
28354         this.fireEvent('valid', this);
28355     },
28356     
28357     getName: function()
28358     {
28359         return this.name;
28360     }
28361     
28362 });
28363
28364  /**
28365  *
28366  * This is based on 
28367  * http://masonry.desandro.com
28368  *
28369  * The idea is to render all the bricks based on vertical width...
28370  *
28371  * The original code extends 'outlayer' - we might need to use that....
28372  * 
28373  */
28374
28375
28376 /**
28377  * @class Roo.bootstrap.LayoutMasonry
28378  * @extends Roo.bootstrap.Component
28379  * Bootstrap Layout Masonry class
28380  * 
28381  * @constructor
28382  * Create a new Element
28383  * @param {Object} config The config object
28384  */
28385
28386 Roo.bootstrap.LayoutMasonry = function(config){
28387     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28388     
28389     this.bricks = [];
28390     
28391 };
28392
28393 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28394     
28395     /**
28396      * @cfg {Boolean} isLayoutInstant = no animation?
28397      */   
28398     isLayoutInstant : false, // needed?
28399    
28400     /**
28401      * @cfg {Number} boxWidth  width of the columns
28402      */   
28403     boxWidth : 450,
28404     
28405       /**
28406      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28407      */   
28408     boxHeight : 0,
28409     
28410     /**
28411      * @cfg {Number} padWidth padding below box..
28412      */   
28413     padWidth : 10, 
28414     
28415     /**
28416      * @cfg {Number} gutter gutter width..
28417      */   
28418     gutter : 10, 
28419     
28420     /**
28421      * @cfg {Boolean} isAutoInitial defalut true
28422      */   
28423     isAutoInitial : true, 
28424     
28425     containerWidth: 0,
28426     
28427     /**
28428      * @cfg {Boolean} isHorizontal defalut false
28429      */   
28430     isHorizontal : false, 
28431
28432     currentSize : null,
28433     
28434     tag: 'div',
28435     
28436     cls: '',
28437     
28438     bricks: null, //CompositeElement
28439     
28440     cols : 1,
28441     
28442     _isLayoutInited : false,
28443     
28444 //    isAlternative : false, // only use for vertical layout...
28445     
28446     /**
28447      * @cfg {Number} alternativePadWidth padding below box..
28448      */   
28449     alternativePadWidth : 50, 
28450     
28451     getAutoCreate : function(){
28452         
28453         var cfg = {
28454             tag: this.tag,
28455             cls: 'blog-masonary-wrapper ' + this.cls,
28456             cn : {
28457                 cls : 'mas-boxes masonary'
28458             }
28459         };
28460         
28461         return cfg;
28462     },
28463     
28464     getChildContainer: function( )
28465     {
28466         if (this.boxesEl) {
28467             return this.boxesEl;
28468         }
28469         
28470         this.boxesEl = this.el.select('.mas-boxes').first();
28471         
28472         return this.boxesEl;
28473     },
28474     
28475     
28476     initEvents : function()
28477     {
28478         var _this = this;
28479         
28480         if(this.isAutoInitial){
28481             Roo.log('hook children rendered');
28482             this.on('childrenrendered', function() {
28483                 Roo.log('children rendered');
28484                 _this.initial();
28485             } ,this);
28486         }
28487     },
28488     
28489     initial : function()
28490     {
28491         this.currentSize = this.el.getBox(true);
28492         
28493         Roo.EventManager.onWindowResize(this.resize, this); 
28494
28495         if(!this.isAutoInitial){
28496             this.layout();
28497             return;
28498         }
28499         
28500         this.layout();
28501         
28502         return;
28503         //this.layout.defer(500,this);
28504         
28505     },
28506     
28507     resize : function()
28508     {
28509         Roo.log('resize');
28510         
28511         var cs = this.el.getBox(true);
28512         
28513         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28514             Roo.log("no change in with or X");
28515             return;
28516         }
28517         
28518         this.currentSize = cs;
28519         
28520         this.layout();
28521         
28522     },
28523     
28524     layout : function()
28525     {   
28526         this._resetLayout();
28527         
28528         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28529         
28530         this.layoutItems( isInstant );
28531       
28532         this._isLayoutInited = true;
28533         
28534     },
28535     
28536     _resetLayout : function()
28537     {
28538         if(this.isHorizontal){
28539             this.horizontalMeasureColumns();
28540             return;
28541         }
28542         
28543         this.verticalMeasureColumns();
28544         
28545     },
28546     
28547     verticalMeasureColumns : function()
28548     {
28549         this.getContainerWidth();
28550         
28551 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28552 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28553 //            return;
28554 //        }
28555         
28556         var boxWidth = this.boxWidth + this.padWidth;
28557         
28558         if(this.containerWidth < this.boxWidth){
28559             boxWidth = this.containerWidth
28560         }
28561         
28562         var containerWidth = this.containerWidth;
28563         
28564         var cols = Math.floor(containerWidth / boxWidth);
28565         
28566         this.cols = Math.max( cols, 1 );
28567         
28568         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28569         
28570         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28571         
28572         this.colWidth = boxWidth + avail - this.padWidth;
28573         
28574         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28575         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28576     },
28577     
28578     horizontalMeasureColumns : function()
28579     {
28580         this.getContainerWidth();
28581         
28582         var boxWidth = this.boxWidth;
28583         
28584         if(this.containerWidth < boxWidth){
28585             boxWidth = this.containerWidth;
28586         }
28587         
28588         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28589         
28590         this.el.setHeight(boxWidth);
28591         
28592     },
28593     
28594     getContainerWidth : function()
28595     {
28596         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28597     },
28598     
28599     layoutItems : function( isInstant )
28600     {
28601         var items = Roo.apply([], this.bricks);
28602         
28603         if(this.isHorizontal){
28604             this._horizontalLayoutItems( items , isInstant );
28605             return;
28606         }
28607         
28608 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28609 //            this._verticalAlternativeLayoutItems( items , isInstant );
28610 //            return;
28611 //        }
28612         
28613         this._verticalLayoutItems( items , isInstant );
28614         
28615     },
28616     
28617     _verticalLayoutItems : function ( items , isInstant)
28618     {
28619         if ( !items || !items.length ) {
28620             return;
28621         }
28622         
28623         var standard = [
28624             ['xs', 'xs', 'xs', 'tall'],
28625             ['xs', 'xs', 'tall'],
28626             ['xs', 'xs', 'sm'],
28627             ['xs', 'xs', 'xs'],
28628             ['xs', 'tall'],
28629             ['xs', 'sm'],
28630             ['xs', 'xs'],
28631             ['xs'],
28632             
28633             ['sm', 'xs', 'xs'],
28634             ['sm', 'xs'],
28635             ['sm'],
28636             
28637             ['tall', 'xs', 'xs', 'xs'],
28638             ['tall', 'xs', 'xs'],
28639             ['tall', 'xs'],
28640             ['tall']
28641             
28642         ];
28643         
28644         var queue = [];
28645         
28646         var boxes = [];
28647         
28648         var box = [];
28649         
28650         Roo.each(items, function(item, k){
28651             
28652             switch (item.size) {
28653                 // these layouts take up a full box,
28654                 case 'md' :
28655                 case 'md-left' :
28656                 case 'md-right' :
28657                 case 'wide' :
28658                     
28659                     if(box.length){
28660                         boxes.push(box);
28661                         box = [];
28662                     }
28663                     
28664                     boxes.push([item]);
28665                     
28666                     break;
28667                     
28668                 case 'xs' :
28669                 case 'sm' :
28670                 case 'tall' :
28671                     
28672                     box.push(item);
28673                     
28674                     break;
28675                 default :
28676                     break;
28677                     
28678             }
28679             
28680         }, this);
28681         
28682         if(box.length){
28683             boxes.push(box);
28684             box = [];
28685         }
28686         
28687         var filterPattern = function(box, length)
28688         {
28689             if(!box.length){
28690                 return;
28691             }
28692             
28693             var match = false;
28694             
28695             var pattern = box.slice(0, length);
28696             
28697             var format = [];
28698             
28699             Roo.each(pattern, function(i){
28700                 format.push(i.size);
28701             }, this);
28702             
28703             Roo.each(standard, function(s){
28704                 
28705                 if(String(s) != String(format)){
28706                     return;
28707                 }
28708                 
28709                 match = true;
28710                 return false;
28711                 
28712             }, this);
28713             
28714             if(!match && length == 1){
28715                 return;
28716             }
28717             
28718             if(!match){
28719                 filterPattern(box, length - 1);
28720                 return;
28721             }
28722                 
28723             queue.push(pattern);
28724
28725             box = box.slice(length, box.length);
28726
28727             filterPattern(box, 4);
28728
28729             return;
28730             
28731         }
28732         
28733         Roo.each(boxes, function(box, k){
28734             
28735             if(!box.length){
28736                 return;
28737             }
28738             
28739             if(box.length == 1){
28740                 queue.push(box);
28741                 return;
28742             }
28743             
28744             filterPattern(box, 4);
28745             
28746         }, this);
28747         
28748         this._processVerticalLayoutQueue( queue, isInstant );
28749         
28750     },
28751     
28752 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28753 //    {
28754 //        if ( !items || !items.length ) {
28755 //            return;
28756 //        }
28757 //
28758 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28759 //        
28760 //    },
28761     
28762     _horizontalLayoutItems : function ( items , isInstant)
28763     {
28764         if ( !items || !items.length || items.length < 3) {
28765             return;
28766         }
28767         
28768         items.reverse();
28769         
28770         var eItems = items.slice(0, 3);
28771         
28772         items = items.slice(3, items.length);
28773         
28774         var standard = [
28775             ['xs', 'xs', 'xs', 'wide'],
28776             ['xs', 'xs', 'wide'],
28777             ['xs', 'xs', 'sm'],
28778             ['xs', 'xs', 'xs'],
28779             ['xs', 'wide'],
28780             ['xs', 'sm'],
28781             ['xs', 'xs'],
28782             ['xs'],
28783             
28784             ['sm', 'xs', 'xs'],
28785             ['sm', 'xs'],
28786             ['sm'],
28787             
28788             ['wide', 'xs', 'xs', 'xs'],
28789             ['wide', 'xs', 'xs'],
28790             ['wide', 'xs'],
28791             ['wide'],
28792             
28793             ['wide-thin']
28794         ];
28795         
28796         var queue = [];
28797         
28798         var boxes = [];
28799         
28800         var box = [];
28801         
28802         Roo.each(items, function(item, k){
28803             
28804             switch (item.size) {
28805                 case 'md' :
28806                 case 'md-left' :
28807                 case 'md-right' :
28808                 case 'tall' :
28809                     
28810                     if(box.length){
28811                         boxes.push(box);
28812                         box = [];
28813                     }
28814                     
28815                     boxes.push([item]);
28816                     
28817                     break;
28818                     
28819                 case 'xs' :
28820                 case 'sm' :
28821                 case 'wide' :
28822                 case 'wide-thin' :
28823                     
28824                     box.push(item);
28825                     
28826                     break;
28827                 default :
28828                     break;
28829                     
28830             }
28831             
28832         }, this);
28833         
28834         if(box.length){
28835             boxes.push(box);
28836             box = [];
28837         }
28838         
28839         var filterPattern = function(box, length)
28840         {
28841             if(!box.length){
28842                 return;
28843             }
28844             
28845             var match = false;
28846             
28847             var pattern = box.slice(0, length);
28848             
28849             var format = [];
28850             
28851             Roo.each(pattern, function(i){
28852                 format.push(i.size);
28853             }, this);
28854             
28855             Roo.each(standard, function(s){
28856                 
28857                 if(String(s) != String(format)){
28858                     return;
28859                 }
28860                 
28861                 match = true;
28862                 return false;
28863                 
28864             }, this);
28865             
28866             if(!match && length == 1){
28867                 return;
28868             }
28869             
28870             if(!match){
28871                 filterPattern(box, length - 1);
28872                 return;
28873             }
28874                 
28875             queue.push(pattern);
28876
28877             box = box.slice(length, box.length);
28878
28879             filterPattern(box, 4);
28880
28881             return;
28882             
28883         }
28884         
28885         Roo.each(boxes, function(box, k){
28886             
28887             if(!box.length){
28888                 return;
28889             }
28890             
28891             if(box.length == 1){
28892                 queue.push(box);
28893                 return;
28894             }
28895             
28896             filterPattern(box, 4);
28897             
28898         }, this);
28899         
28900         
28901         var prune = [];
28902         
28903         var pos = this.el.getBox(true);
28904         
28905         var minX = pos.x;
28906         
28907         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28908         
28909         var hit_end = false;
28910         
28911         Roo.each(queue, function(box){
28912             
28913             if(hit_end){
28914                 
28915                 Roo.each(box, function(b){
28916                 
28917                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28918                     b.el.hide();
28919
28920                 }, this);
28921
28922                 return;
28923             }
28924             
28925             var mx = 0;
28926             
28927             Roo.each(box, function(b){
28928                 
28929                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28930                 b.el.show();
28931
28932                 mx = Math.max(mx, b.x);
28933                 
28934             }, this);
28935             
28936             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
28937             
28938             if(maxX < minX){
28939                 
28940                 Roo.each(box, function(b){
28941                 
28942                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
28943                     b.el.hide();
28944                     
28945                 }, this);
28946                 
28947                 hit_end = true;
28948                 
28949                 return;
28950             }
28951             
28952             prune.push(box);
28953             
28954         }, this);
28955         
28956         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
28957     },
28958     
28959     /** Sets position of item in DOM
28960     * @param {Element} item
28961     * @param {Number} x - horizontal position
28962     * @param {Number} y - vertical position
28963     * @param {Boolean} isInstant - disables transitions
28964     */
28965     _processVerticalLayoutQueue : function( queue, isInstant )
28966     {
28967         var pos = this.el.getBox(true);
28968         var x = pos.x;
28969         var y = pos.y;
28970         var maxY = [];
28971         
28972         for (var i = 0; i < this.cols; i++){
28973             maxY[i] = pos.y;
28974         }
28975         
28976         Roo.each(queue, function(box, k){
28977             
28978             var col = k % this.cols;
28979             
28980             Roo.each(box, function(b,kk){
28981                 
28982                 b.el.position('absolute');
28983                 
28984                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
28985                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
28986                 
28987                 if(b.size == 'md-left' || b.size == 'md-right'){
28988                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
28989                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
28990                 }
28991                 
28992                 b.el.setWidth(width);
28993                 b.el.setHeight(height);
28994                 
28995             }, this);
28996             
28997             for (var i = 0; i < this.cols; i++){
28998                 
28999                 if(maxY[i] < maxY[col]){
29000                     col = i;
29001                     continue;
29002                 }
29003                 
29004                 col = Math.min(col, i);
29005                 
29006             }
29007             
29008             x = pos.x + col * (this.colWidth + this.padWidth);
29009             
29010             y = maxY[col];
29011             
29012             var positions = [];
29013             
29014             switch (box.length){
29015                 case 1 :
29016                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29017                     break;
29018                 case 2 :
29019                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29020                     break;
29021                 case 3 :
29022                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29023                     break;
29024                 case 4 :
29025                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29026                     break;
29027                 default :
29028                     break;
29029             }
29030             
29031             Roo.each(box, function(b,kk){
29032                 
29033                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29034                 
29035                 var sz = b.el.getSize();
29036                 
29037                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29038                 
29039             }, this);
29040             
29041         }, this);
29042         
29043         var mY = 0;
29044         
29045         for (var i = 0; i < this.cols; i++){
29046             mY = Math.max(mY, maxY[i]);
29047         }
29048         
29049         this.el.setHeight(mY - pos.y);
29050         
29051     },
29052     
29053 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29054 //    {
29055 //        var pos = this.el.getBox(true);
29056 //        var x = pos.x;
29057 //        var y = pos.y;
29058 //        var maxX = pos.right;
29059 //        
29060 //        var maxHeight = 0;
29061 //        
29062 //        Roo.each(items, function(item, k){
29063 //            
29064 //            var c = k % 2;
29065 //            
29066 //            item.el.position('absolute');
29067 //                
29068 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29069 //
29070 //            item.el.setWidth(width);
29071 //
29072 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29073 //
29074 //            item.el.setHeight(height);
29075 //            
29076 //            if(c == 0){
29077 //                item.el.setXY([x, y], isInstant ? false : true);
29078 //            } else {
29079 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29080 //            }
29081 //            
29082 //            y = y + height + this.alternativePadWidth;
29083 //            
29084 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29085 //            
29086 //        }, this);
29087 //        
29088 //        this.el.setHeight(maxHeight);
29089 //        
29090 //    },
29091     
29092     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29093     {
29094         var pos = this.el.getBox(true);
29095         
29096         var minX = pos.x;
29097         var minY = pos.y;
29098         
29099         var maxX = pos.right;
29100         
29101         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29102         
29103         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29104         
29105         Roo.each(queue, function(box, k){
29106             
29107             Roo.each(box, function(b, kk){
29108                 
29109                 b.el.position('absolute');
29110                 
29111                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29112                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29113                 
29114                 if(b.size == 'md-left' || b.size == 'md-right'){
29115                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29116                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29117                 }
29118                 
29119                 b.el.setWidth(width);
29120                 b.el.setHeight(height);
29121                 
29122             }, this);
29123             
29124             if(!box.length){
29125                 return;
29126             }
29127             
29128             var positions = [];
29129             
29130             switch (box.length){
29131                 case 1 :
29132                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29133                     break;
29134                 case 2 :
29135                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29136                     break;
29137                 case 3 :
29138                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29139                     break;
29140                 case 4 :
29141                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29142                     break;
29143                 default :
29144                     break;
29145             }
29146             
29147             Roo.each(box, function(b,kk){
29148                 
29149                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29150                 
29151                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29152                 
29153             }, this);
29154             
29155         }, this);
29156         
29157     },
29158     
29159     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29160     {
29161         Roo.each(eItems, function(b,k){
29162             
29163             b.size = (k == 0) ? 'sm' : 'xs';
29164             b.x = (k == 0) ? 2 : 1;
29165             b.y = (k == 0) ? 2 : 1;
29166             
29167             b.el.position('absolute');
29168             
29169             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29170                 
29171             b.el.setWidth(width);
29172             
29173             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29174             
29175             b.el.setHeight(height);
29176             
29177         }, this);
29178
29179         var positions = [];
29180         
29181         positions.push({
29182             x : maxX - this.unitWidth * 2 - this.gutter,
29183             y : minY
29184         });
29185         
29186         positions.push({
29187             x : maxX - this.unitWidth,
29188             y : minY + (this.unitWidth + this.gutter) * 2
29189         });
29190         
29191         positions.push({
29192             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29193             y : minY
29194         });
29195         
29196         Roo.each(eItems, function(b,k){
29197             
29198             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29199
29200         }, this);
29201         
29202     },
29203     
29204     getVerticalOneBoxColPositions : function(x, y, box)
29205     {
29206         var pos = [];
29207         
29208         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29209         
29210         if(box[0].size == 'md-left'){
29211             rand = 0;
29212         }
29213         
29214         if(box[0].size == 'md-right'){
29215             rand = 1;
29216         }
29217         
29218         pos.push({
29219             x : x + (this.unitWidth + this.gutter) * rand,
29220             y : y
29221         });
29222         
29223         return pos;
29224     },
29225     
29226     getVerticalTwoBoxColPositions : function(x, y, box)
29227     {
29228         var pos = [];
29229         
29230         if(box[0].size == 'xs'){
29231             
29232             pos.push({
29233                 x : x,
29234                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29235             });
29236
29237             pos.push({
29238                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29239                 y : y
29240             });
29241             
29242             return pos;
29243             
29244         }
29245         
29246         pos.push({
29247             x : x,
29248             y : y
29249         });
29250
29251         pos.push({
29252             x : x + (this.unitWidth + this.gutter) * 2,
29253             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29254         });
29255         
29256         return pos;
29257         
29258     },
29259     
29260     getVerticalThreeBoxColPositions : function(x, y, box)
29261     {
29262         var pos = [];
29263         
29264         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29265             
29266             pos.push({
29267                 x : x,
29268                 y : y
29269             });
29270
29271             pos.push({
29272                 x : x + (this.unitWidth + this.gutter) * 1,
29273                 y : y
29274             });
29275             
29276             pos.push({
29277                 x : x + (this.unitWidth + this.gutter) * 2,
29278                 y : y
29279             });
29280             
29281             return pos;
29282             
29283         }
29284         
29285         if(box[0].size == 'xs' && box[1].size == 'xs'){
29286             
29287             pos.push({
29288                 x : x,
29289                 y : y
29290             });
29291
29292             pos.push({
29293                 x : x,
29294                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29295             });
29296             
29297             pos.push({
29298                 x : x + (this.unitWidth + this.gutter) * 1,
29299                 y : y
29300             });
29301             
29302             return pos;
29303             
29304         }
29305         
29306         pos.push({
29307             x : x,
29308             y : y
29309         });
29310
29311         pos.push({
29312             x : x + (this.unitWidth + this.gutter) * 2,
29313             y : y
29314         });
29315
29316         pos.push({
29317             x : x + (this.unitWidth + this.gutter) * 2,
29318             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29319         });
29320             
29321         return pos;
29322         
29323     },
29324     
29325     getVerticalFourBoxColPositions : function(x, y, box)
29326     {
29327         var pos = [];
29328         
29329         if(box[0].size == 'xs'){
29330             
29331             pos.push({
29332                 x : x,
29333                 y : y
29334             });
29335
29336             pos.push({
29337                 x : x,
29338                 y : y + (this.unitHeight + this.gutter) * 1
29339             });
29340             
29341             pos.push({
29342                 x : x,
29343                 y : y + (this.unitHeight + this.gutter) * 2
29344             });
29345             
29346             pos.push({
29347                 x : x + (this.unitWidth + this.gutter) * 1,
29348                 y : y
29349             });
29350             
29351             return pos;
29352             
29353         }
29354         
29355         pos.push({
29356             x : x,
29357             y : y
29358         });
29359
29360         pos.push({
29361             x : x + (this.unitWidth + this.gutter) * 2,
29362             y : y
29363         });
29364
29365         pos.push({
29366             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29367             y : y + (this.unitHeight + this.gutter) * 1
29368         });
29369
29370         pos.push({
29371             x : x + (this.unitWidth + this.gutter) * 2,
29372             y : y + (this.unitWidth + this.gutter) * 2
29373         });
29374
29375         return pos;
29376         
29377     },
29378     
29379     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29380     {
29381         var pos = [];
29382         
29383         if(box[0].size == 'md-left'){
29384             pos.push({
29385                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29386                 y : minY
29387             });
29388             
29389             return pos;
29390         }
29391         
29392         if(box[0].size == 'md-right'){
29393             pos.push({
29394                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29395                 y : minY + (this.unitWidth + this.gutter) * 1
29396             });
29397             
29398             return pos;
29399         }
29400         
29401         var rand = Math.floor(Math.random() * (4 - box[0].y));
29402         
29403         pos.push({
29404             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29405             y : minY + (this.unitWidth + this.gutter) * rand
29406         });
29407         
29408         return pos;
29409         
29410     },
29411     
29412     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29413     {
29414         var pos = [];
29415         
29416         if(box[0].size == 'xs'){
29417             
29418             pos.push({
29419                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29420                 y : minY
29421             });
29422
29423             pos.push({
29424                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29425                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29426             });
29427             
29428             return pos;
29429             
29430         }
29431         
29432         pos.push({
29433             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29434             y : minY
29435         });
29436
29437         pos.push({
29438             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29439             y : minY + (this.unitWidth + this.gutter) * 2
29440         });
29441         
29442         return pos;
29443         
29444     },
29445     
29446     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29447     {
29448         var pos = [];
29449         
29450         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29451             
29452             pos.push({
29453                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29454                 y : minY
29455             });
29456
29457             pos.push({
29458                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29459                 y : minY + (this.unitWidth + this.gutter) * 1
29460             });
29461             
29462             pos.push({
29463                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29464                 y : minY + (this.unitWidth + this.gutter) * 2
29465             });
29466             
29467             return pos;
29468             
29469         }
29470         
29471         if(box[0].size == 'xs' && box[1].size == 'xs'){
29472             
29473             pos.push({
29474                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29475                 y : minY
29476             });
29477
29478             pos.push({
29479                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29480                 y : minY
29481             });
29482             
29483             pos.push({
29484                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29485                 y : minY + (this.unitWidth + this.gutter) * 1
29486             });
29487             
29488             return pos;
29489             
29490         }
29491         
29492         pos.push({
29493             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29494             y : minY
29495         });
29496
29497         pos.push({
29498             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29499             y : minY + (this.unitWidth + this.gutter) * 2
29500         });
29501
29502         pos.push({
29503             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29504             y : minY + (this.unitWidth + this.gutter) * 2
29505         });
29506             
29507         return pos;
29508         
29509     },
29510     
29511     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29512     {
29513         var pos = [];
29514         
29515         if(box[0].size == 'xs'){
29516             
29517             pos.push({
29518                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29519                 y : minY
29520             });
29521
29522             pos.push({
29523                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29524                 y : minY
29525             });
29526             
29527             pos.push({
29528                 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),
29529                 y : minY
29530             });
29531             
29532             pos.push({
29533                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29534                 y : minY + (this.unitWidth + this.gutter) * 1
29535             });
29536             
29537             return pos;
29538             
29539         }
29540         
29541         pos.push({
29542             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29543             y : minY
29544         });
29545         
29546         pos.push({
29547             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29548             y : minY + (this.unitWidth + this.gutter) * 2
29549         });
29550         
29551         pos.push({
29552             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29553             y : minY + (this.unitWidth + this.gutter) * 2
29554         });
29555         
29556         pos.push({
29557             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),
29558             y : minY + (this.unitWidth + this.gutter) * 2
29559         });
29560
29561         return pos;
29562         
29563     }
29564     
29565 });
29566
29567  
29568
29569  /**
29570  *
29571  * This is based on 
29572  * http://masonry.desandro.com
29573  *
29574  * The idea is to render all the bricks based on vertical width...
29575  *
29576  * The original code extends 'outlayer' - we might need to use that....
29577  * 
29578  */
29579
29580
29581 /**
29582  * @class Roo.bootstrap.LayoutMasonryAuto
29583  * @extends Roo.bootstrap.Component
29584  * Bootstrap Layout Masonry class
29585  * 
29586  * @constructor
29587  * Create a new Element
29588  * @param {Object} config The config object
29589  */
29590
29591 Roo.bootstrap.LayoutMasonryAuto = function(config){
29592     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29593 };
29594
29595 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29596     
29597       /**
29598      * @cfg {Boolean} isFitWidth  - resize the width..
29599      */   
29600     isFitWidth : false,  // options..
29601     /**
29602      * @cfg {Boolean} isOriginLeft = left align?
29603      */   
29604     isOriginLeft : true,
29605     /**
29606      * @cfg {Boolean} isOriginTop = top align?
29607      */   
29608     isOriginTop : false,
29609     /**
29610      * @cfg {Boolean} isLayoutInstant = no animation?
29611      */   
29612     isLayoutInstant : false, // needed?
29613     /**
29614      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29615      */   
29616     isResizingContainer : true,
29617     /**
29618      * @cfg {Number} columnWidth  width of the columns 
29619      */   
29620     
29621     columnWidth : 0,
29622     /**
29623      * @cfg {Number} padHeight padding below box..
29624      */   
29625     
29626     padHeight : 10, 
29627     
29628     /**
29629      * @cfg {Boolean} isAutoInitial defalut true
29630      */   
29631     
29632     isAutoInitial : true, 
29633     
29634     // private?
29635     gutter : 0,
29636     
29637     containerWidth: 0,
29638     initialColumnWidth : 0,
29639     currentSize : null,
29640     
29641     colYs : null, // array.
29642     maxY : 0,
29643     padWidth: 10,
29644     
29645     
29646     tag: 'div',
29647     cls: '',
29648     bricks: null, //CompositeElement
29649     cols : 0, // array?
29650     // element : null, // wrapped now this.el
29651     _isLayoutInited : null, 
29652     
29653     
29654     getAutoCreate : function(){
29655         
29656         var cfg = {
29657             tag: this.tag,
29658             cls: 'blog-masonary-wrapper ' + this.cls,
29659             cn : {
29660                 cls : 'mas-boxes masonary'
29661             }
29662         };
29663         
29664         return cfg;
29665     },
29666     
29667     getChildContainer: function( )
29668     {
29669         if (this.boxesEl) {
29670             return this.boxesEl;
29671         }
29672         
29673         this.boxesEl = this.el.select('.mas-boxes').first();
29674         
29675         return this.boxesEl;
29676     },
29677     
29678     
29679     initEvents : function()
29680     {
29681         var _this = this;
29682         
29683         if(this.isAutoInitial){
29684             Roo.log('hook children rendered');
29685             this.on('childrenrendered', function() {
29686                 Roo.log('children rendered');
29687                 _this.initial();
29688             } ,this);
29689         }
29690         
29691     },
29692     
29693     initial : function()
29694     {
29695         this.reloadItems();
29696
29697         this.currentSize = this.el.getBox(true);
29698
29699         /// was window resize... - let's see if this works..
29700         Roo.EventManager.onWindowResize(this.resize, this); 
29701
29702         if(!this.isAutoInitial){
29703             this.layout();
29704             return;
29705         }
29706         
29707         this.layout.defer(500,this);
29708     },
29709     
29710     reloadItems: function()
29711     {
29712         this.bricks = this.el.select('.masonry-brick', true);
29713         
29714         this.bricks.each(function(b) {
29715             //Roo.log(b.getSize());
29716             if (!b.attr('originalwidth')) {
29717                 b.attr('originalwidth',  b.getSize().width);
29718             }
29719             
29720         });
29721         
29722         Roo.log(this.bricks.elements.length);
29723     },
29724     
29725     resize : function()
29726     {
29727         Roo.log('resize');
29728         var cs = this.el.getBox(true);
29729         
29730         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29731             Roo.log("no change in with or X");
29732             return;
29733         }
29734         this.currentSize = cs;
29735         this.layout();
29736     },
29737     
29738     layout : function()
29739     {
29740          Roo.log('layout');
29741         this._resetLayout();
29742         //this._manageStamps();
29743       
29744         // don't animate first layout
29745         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29746         this.layoutItems( isInstant );
29747       
29748         // flag for initalized
29749         this._isLayoutInited = true;
29750     },
29751     
29752     layoutItems : function( isInstant )
29753     {
29754         //var items = this._getItemsForLayout( this.items );
29755         // original code supports filtering layout items.. we just ignore it..
29756         
29757         this._layoutItems( this.bricks , isInstant );
29758       
29759         this._postLayout();
29760     },
29761     _layoutItems : function ( items , isInstant)
29762     {
29763        //this.fireEvent( 'layout', this, items );
29764     
29765
29766         if ( !items || !items.elements.length ) {
29767           // no items, emit event with empty array
29768             return;
29769         }
29770
29771         var queue = [];
29772         items.each(function(item) {
29773             Roo.log("layout item");
29774             Roo.log(item);
29775             // get x/y object from method
29776             var position = this._getItemLayoutPosition( item );
29777             // enqueue
29778             position.item = item;
29779             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29780             queue.push( position );
29781         }, this);
29782       
29783         this._processLayoutQueue( queue );
29784     },
29785     /** Sets position of item in DOM
29786     * @param {Element} item
29787     * @param {Number} x - horizontal position
29788     * @param {Number} y - vertical position
29789     * @param {Boolean} isInstant - disables transitions
29790     */
29791     _processLayoutQueue : function( queue )
29792     {
29793         for ( var i=0, len = queue.length; i < len; i++ ) {
29794             var obj = queue[i];
29795             obj.item.position('absolute');
29796             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29797         }
29798     },
29799       
29800     
29801     /**
29802     * Any logic you want to do after each layout,
29803     * i.e. size the container
29804     */
29805     _postLayout : function()
29806     {
29807         this.resizeContainer();
29808     },
29809     
29810     resizeContainer : function()
29811     {
29812         if ( !this.isResizingContainer ) {
29813             return;
29814         }
29815         var size = this._getContainerSize();
29816         if ( size ) {
29817             this.el.setSize(size.width,size.height);
29818             this.boxesEl.setSize(size.width,size.height);
29819         }
29820     },
29821     
29822     
29823     
29824     _resetLayout : function()
29825     {
29826         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29827         this.colWidth = this.el.getWidth();
29828         //this.gutter = this.el.getWidth(); 
29829         
29830         this.measureColumns();
29831
29832         // reset column Y
29833         var i = this.cols;
29834         this.colYs = [];
29835         while (i--) {
29836             this.colYs.push( 0 );
29837         }
29838     
29839         this.maxY = 0;
29840     },
29841
29842     measureColumns : function()
29843     {
29844         this.getContainerWidth();
29845       // if columnWidth is 0, default to outerWidth of first item
29846         if ( !this.columnWidth ) {
29847             var firstItem = this.bricks.first();
29848             Roo.log(firstItem);
29849             this.columnWidth  = this.containerWidth;
29850             if (firstItem && firstItem.attr('originalwidth') ) {
29851                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29852             }
29853             // columnWidth fall back to item of first element
29854             Roo.log("set column width?");
29855                         this.initialColumnWidth = this.columnWidth  ;
29856
29857             // if first elem has no width, default to size of container
29858             
29859         }
29860         
29861         
29862         if (this.initialColumnWidth) {
29863             this.columnWidth = this.initialColumnWidth;
29864         }
29865         
29866         
29867             
29868         // column width is fixed at the top - however if container width get's smaller we should
29869         // reduce it...
29870         
29871         // this bit calcs how man columns..
29872             
29873         var columnWidth = this.columnWidth += this.gutter;
29874       
29875         // calculate columns
29876         var containerWidth = this.containerWidth + this.gutter;
29877         
29878         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29879         // fix rounding errors, typically with gutters
29880         var excess = columnWidth - containerWidth % columnWidth;
29881         
29882         
29883         // if overshoot is less than a pixel, round up, otherwise floor it
29884         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29885         cols = Math[ mathMethod ]( cols );
29886         this.cols = Math.max( cols, 1 );
29887         
29888         
29889          // padding positioning..
29890         var totalColWidth = this.cols * this.columnWidth;
29891         var padavail = this.containerWidth - totalColWidth;
29892         // so for 2 columns - we need 3 'pads'
29893         
29894         var padNeeded = (1+this.cols) * this.padWidth;
29895         
29896         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29897         
29898         this.columnWidth += padExtra
29899         //this.padWidth = Math.floor(padavail /  ( this.cols));
29900         
29901         // adjust colum width so that padding is fixed??
29902         
29903         // we have 3 columns ... total = width * 3
29904         // we have X left over... that should be used by 
29905         
29906         //if (this.expandC) {
29907             
29908         //}
29909         
29910         
29911         
29912     },
29913     
29914     getContainerWidth : function()
29915     {
29916        /* // container is parent if fit width
29917         var container = this.isFitWidth ? this.element.parentNode : this.element;
29918         // check that this.size and size are there
29919         // IE8 triggers resize on body size change, so they might not be
29920         
29921         var size = getSize( container );  //FIXME
29922         this.containerWidth = size && size.innerWidth; //FIXME
29923         */
29924          
29925         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29926         
29927     },
29928     
29929     _getItemLayoutPosition : function( item )  // what is item?
29930     {
29931         // we resize the item to our columnWidth..
29932       
29933         item.setWidth(this.columnWidth);
29934         item.autoBoxAdjust  = false;
29935         
29936         var sz = item.getSize();
29937  
29938         // how many columns does this brick span
29939         var remainder = this.containerWidth % this.columnWidth;
29940         
29941         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
29942         // round if off by 1 pixel, otherwise use ceil
29943         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
29944         colSpan = Math.min( colSpan, this.cols );
29945         
29946         // normally this should be '1' as we dont' currently allow multi width columns..
29947         
29948         var colGroup = this._getColGroup( colSpan );
29949         // get the minimum Y value from the columns
29950         var minimumY = Math.min.apply( Math, colGroup );
29951         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29952         
29953         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
29954          
29955         // position the brick
29956         var position = {
29957             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
29958             y: this.currentSize.y + minimumY + this.padHeight
29959         };
29960         
29961         Roo.log(position);
29962         // apply setHeight to necessary columns
29963         var setHeight = minimumY + sz.height + this.padHeight;
29964         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
29965         
29966         var setSpan = this.cols + 1 - colGroup.length;
29967         for ( var i = 0; i < setSpan; i++ ) {
29968           this.colYs[ shortColIndex + i ] = setHeight ;
29969         }
29970       
29971         return position;
29972     },
29973     
29974     /**
29975      * @param {Number} colSpan - number of columns the element spans
29976      * @returns {Array} colGroup
29977      */
29978     _getColGroup : function( colSpan )
29979     {
29980         if ( colSpan < 2 ) {
29981           // if brick spans only one column, use all the column Ys
29982           return this.colYs;
29983         }
29984       
29985         var colGroup = [];
29986         // how many different places could this brick fit horizontally
29987         var groupCount = this.cols + 1 - colSpan;
29988         // for each group potential horizontal position
29989         for ( var i = 0; i < groupCount; i++ ) {
29990           // make an array of colY values for that one group
29991           var groupColYs = this.colYs.slice( i, i + colSpan );
29992           // and get the max value of the array
29993           colGroup[i] = Math.max.apply( Math, groupColYs );
29994         }
29995         return colGroup;
29996     },
29997     /*
29998     _manageStamp : function( stamp )
29999     {
30000         var stampSize =  stamp.getSize();
30001         var offset = stamp.getBox();
30002         // get the columns that this stamp affects
30003         var firstX = this.isOriginLeft ? offset.x : offset.right;
30004         var lastX = firstX + stampSize.width;
30005         var firstCol = Math.floor( firstX / this.columnWidth );
30006         firstCol = Math.max( 0, firstCol );
30007         
30008         var lastCol = Math.floor( lastX / this.columnWidth );
30009         // lastCol should not go over if multiple of columnWidth #425
30010         lastCol -= lastX % this.columnWidth ? 0 : 1;
30011         lastCol = Math.min( this.cols - 1, lastCol );
30012         
30013         // set colYs to bottom of the stamp
30014         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30015             stampSize.height;
30016             
30017         for ( var i = firstCol; i <= lastCol; i++ ) {
30018           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30019         }
30020     },
30021     */
30022     
30023     _getContainerSize : function()
30024     {
30025         this.maxY = Math.max.apply( Math, this.colYs );
30026         var size = {
30027             height: this.maxY
30028         };
30029       
30030         if ( this.isFitWidth ) {
30031             size.width = this._getContainerFitWidth();
30032         }
30033       
30034         return size;
30035     },
30036     
30037     _getContainerFitWidth : function()
30038     {
30039         var unusedCols = 0;
30040         // count unused columns
30041         var i = this.cols;
30042         while ( --i ) {
30043           if ( this.colYs[i] !== 0 ) {
30044             break;
30045           }
30046           unusedCols++;
30047         }
30048         // fit container to columns that have been used
30049         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30050     },
30051     
30052     needsResizeLayout : function()
30053     {
30054         var previousWidth = this.containerWidth;
30055         this.getContainerWidth();
30056         return previousWidth !== this.containerWidth;
30057     }
30058  
30059 });
30060
30061  
30062
30063  /*
30064  * - LGPL
30065  *
30066  * element
30067  * 
30068  */
30069
30070 /**
30071  * @class Roo.bootstrap.MasonryBrick
30072  * @extends Roo.bootstrap.Component
30073  * Bootstrap MasonryBrick class
30074  * 
30075  * @constructor
30076  * Create a new MasonryBrick
30077  * @param {Object} config The config object
30078  */
30079
30080 Roo.bootstrap.MasonryBrick = function(config){
30081     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30082     
30083     this.addEvents({
30084         // raw events
30085         /**
30086          * @event click
30087          * When a MasonryBrick is clcik
30088          * @param {Roo.bootstrap.MasonryBrick} this
30089          * @param {Roo.EventObject} e
30090          */
30091         "click" : true
30092     });
30093 };
30094
30095 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30096     
30097     /**
30098      * @cfg {String} title
30099      */   
30100     title : '',
30101     /**
30102      * @cfg {String} html
30103      */   
30104     html : '',
30105     /**
30106      * @cfg {String} bgimage
30107      */   
30108     bgimage : '',
30109     /**
30110      * @cfg {String} cls
30111      */   
30112     cls : '',
30113     /**
30114      * @cfg {String} href
30115      */   
30116     href : '',
30117     /**
30118      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30119      */   
30120     size : 'xs',
30121     
30122     /**
30123      * @cfg {String} (center|bottom) placetitle
30124      */   
30125     placetitle : '',
30126     
30127     getAutoCreate : function()
30128     {
30129         var cls = 'masonry-brick';
30130         
30131         if(this.href.length){
30132             cls += ' masonry-brick-link';
30133         }
30134         
30135         if(this.bgimage.length){
30136             cls += ' masonry-brick-image';
30137         }
30138         
30139         if(this.size){
30140             cls += ' masonry-' + this.size + '-brick';
30141         }
30142         
30143         if(this.placetitle.length){
30144             
30145             switch (this.placetitle) {
30146                 case 'center' :
30147                     cls += ' masonry-center-title';
30148                     break;
30149                 case 'bottom' :
30150                     cls += ' masonry-bottom-title';
30151                     break;
30152                 default:
30153                     break;
30154             }
30155             
30156         } else {
30157             if(!this.html.length && !this.bgimage.length){
30158                 cls += ' masonry-center-title';
30159             }
30160
30161             if(!this.html.length && this.bgimage.length){
30162                 cls += ' masonry-bottom-title';
30163             }
30164         }
30165         
30166         if(this.cls){
30167             cls += ' ' + this.cls;
30168         }
30169         
30170         var cfg = {
30171             tag: (this.href.length) ? 'a' : 'div',
30172             cls: cls,
30173             cn: [
30174                 {
30175                     tag: 'div',
30176                     cls: 'masonry-brick-paragraph',
30177                     cn: []
30178                 }
30179             ]
30180         };
30181         
30182         if(this.href.length){
30183             cfg.href = this.href;
30184         }
30185         
30186         var cn = cfg.cn[0].cn;
30187         
30188         if(this.title.length){
30189             cn.push({
30190                 tag: 'h4',
30191                 cls: 'masonry-brick-title',
30192                 html: this.title
30193             });
30194         }
30195         
30196         if(this.html.length){
30197             cn.push({
30198                 tag: 'p',
30199                 cls: 'masonry-brick-text',
30200                 html: this.html
30201             });
30202         }
30203         
30204         if(this.bgimage.length){
30205             cfg.cn.push({
30206                 tag: 'img',
30207                 cls: 'masonry-brick-image-view',
30208                 src: this.bgimage
30209             });
30210         }
30211         
30212         return cfg;
30213         
30214     },
30215     
30216     initEvents: function() 
30217     {
30218         switch (this.size) {
30219             case 'xs' :
30220 //                this.intSize = 1;
30221                 this.x = 1;
30222                 this.y = 1;
30223                 break;
30224             case 'sm' :
30225 //                this.intSize = 2;
30226                 this.x = 2;
30227                 this.y = 2;
30228                 break;
30229             case 'md' :
30230             case 'md-left' :
30231             case 'md-right' :
30232 //                this.intSize = 3;
30233                 this.x = 3;
30234                 this.y = 3;
30235                 break;
30236             case 'tall' :
30237 //                this.intSize = 3;
30238                 this.x = 2;
30239                 this.y = 3;
30240                 break;
30241             case 'wide' :
30242 //                this.intSize = 3;
30243                 this.x = 3;
30244                 this.y = 2;
30245                 break;
30246             case 'wide-thin' :
30247 //                this.intSize = 3;
30248                 this.x = 3;
30249                 this.y = 1;
30250                 break;
30251                         
30252             default :
30253                 break;
30254         }
30255         
30256         
30257         
30258         if(Roo.isTouch){
30259             this.el.on('touchstart', this.onTouchStart, this);
30260             this.el.on('touchmove', this.onTouchMove, this);
30261             this.el.on('touchend', this.onTouchEnd, this);
30262         } else {
30263             this.el.on('mouseenter'  ,this.enter, this);
30264             this.el.on('mouseleave', this.leave, this);
30265         }
30266         
30267         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30268             this.parent().bricks.push(this);   
30269         }
30270         
30271     },
30272     
30273     onClick: function(e, el)
30274     {
30275         alert('click');
30276         
30277         if(!Roo.isTouch){
30278             return;
30279         }
30280         
30281         var time = this.endTimer - this.startTimer;
30282         
30283         alert(time);
30284         
30285         if(time < 1000){
30286             return;
30287         }
30288         
30289         e.preventDefault();
30290     },
30291     
30292     enter: function(e, el)
30293     {
30294         e.preventDefault();
30295         
30296         if(this.bgimage.length && this.html.length){
30297             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30298         }
30299     },
30300     
30301     leave: function(e, el)
30302     {
30303         e.preventDefault();
30304         
30305         if(this.bgimage.length && this.html.length){
30306             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30307         }
30308     },
30309     
30310     onTouchStart: function(e, el)
30311     {
30312 //        e.preventDefault();
30313         
30314         if(!this.bgimage.length || !this.html.length){
30315             return;
30316         }
30317         
30318         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30319         
30320         this.timer = new Date().getTime();
30321         
30322         this.touchmoved = false;
30323     },
30324     
30325     onTouchMove: function(e, el)
30326     {
30327         this.touchmoved = true;
30328     },
30329     
30330     onTouchEnd: function(e, el)
30331     {
30332 //        e.preventDefault();
30333         
30334         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30335             return;
30336         }
30337         
30338         if(!this.bgimage.length || !this.html.length){
30339             
30340             if(this.href.length){
30341                 window.location.href = this.href;
30342             }
30343             
30344             return;
30345         }
30346         
30347         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30348         
30349         window.location.href = this.href;
30350     }
30351     
30352 });
30353
30354  
30355
30356  /*
30357  * - LGPL
30358  *
30359  * element
30360  * 
30361  */
30362
30363 /**
30364  * @class Roo.bootstrap.Brick
30365  * @extends Roo.bootstrap.Component
30366  * Bootstrap Brick class
30367  * 
30368  * @constructor
30369  * Create a new Brick
30370  * @param {Object} config The config object
30371  */
30372
30373 Roo.bootstrap.Brick = function(config){
30374     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30375     
30376     this.addEvents({
30377         // raw events
30378         /**
30379          * @event click
30380          * When a Brick is click
30381          * @param {Roo.bootstrap.Brick} this
30382          * @param {Roo.EventObject} e
30383          */
30384         "click" : true
30385     });
30386 };
30387
30388 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30389     
30390     /**
30391      * @cfg {String} title
30392      */   
30393     title : '',
30394     /**
30395      * @cfg {String} html
30396      */   
30397     html : '',
30398     /**
30399      * @cfg {String} bgimage
30400      */   
30401     bgimage : '',
30402     /**
30403      * @cfg {String} cls
30404      */   
30405     cls : '',
30406     /**
30407      * @cfg {String} href
30408      */   
30409     href : '',
30410     /**
30411      * @cfg {String} video
30412      */   
30413     video : '',
30414     /**
30415      * @cfg {Boolean} square
30416      */   
30417     square : true,
30418     
30419     getAutoCreate : function()
30420     {
30421         var cls = 'roo-brick';
30422         
30423         if(this.href.length){
30424             cls += ' roo-brick-link';
30425         }
30426         
30427         if(this.bgimage.length){
30428             cls += ' roo-brick-image';
30429         }
30430         
30431         if(!this.html.length && !this.bgimage.length){
30432             cls += ' roo-brick-center-title';
30433         }
30434         
30435         if(!this.html.length && this.bgimage.length){
30436             cls += ' roo-brick-bottom-title';
30437         }
30438         
30439         if(this.cls){
30440             cls += ' ' + this.cls;
30441         }
30442         
30443         var cfg = {
30444             tag: (this.href.length) ? 'a' : 'div',
30445             cls: cls,
30446             cn: [
30447                 {
30448                     tag: 'div',
30449                     cls: 'roo-brick-paragraph',
30450                     cn: []
30451                 }
30452             ]
30453         };
30454         
30455         if(this.href.length){
30456             cfg.href = this.href;
30457         }
30458         
30459         var cn = cfg.cn[0].cn;
30460         
30461         if(this.title.length){
30462             cn.push({
30463                 tag: 'h4',
30464                 cls: 'roo-brick-title',
30465                 html: this.title
30466             });
30467         }
30468         
30469         if(this.html.length){
30470             cn.push({
30471                 tag: 'p',
30472                 cls: 'roo-brick-text',
30473                 html: this.html
30474             });
30475         }
30476         
30477         if(this.bgimage.length){
30478             cfg.cn.push({
30479                 tag: 'img',
30480                 cls: 'roo-brick-image-view',
30481                 src: this.bgimage
30482             });
30483         }
30484         
30485         return cfg;
30486     },
30487     
30488     initEvents: function() 
30489     {
30490         if(this.title.length || this.html.length){
30491             this.el.on('mouseenter'  ,this.enter, this);
30492             this.el.on('mouseleave', this.leave, this);
30493         }
30494         
30495         
30496         Roo.EventManager.onWindowResize(this.resize, this); 
30497         
30498         this.resize();
30499     },
30500     
30501     resize : function()
30502     {
30503         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30504         
30505         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30506 //        paragraph.setHeight(paragraph.getWidth());
30507         
30508         if(this.bgimage.length){
30509             var image = this.el.select('.roo-brick-image-view', true).first();
30510             image.setWidth(paragraph.getWidth());
30511             image.setHeight(paragraph.getWidth());
30512         }
30513         
30514     },
30515     
30516     enter: function(e, el)
30517     {
30518         e.preventDefault();
30519         
30520         if(this.bgimage.length){
30521             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30522             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30523         }
30524     },
30525     
30526     leave: function(e, el)
30527     {
30528         e.preventDefault();
30529         
30530         if(this.bgimage.length){
30531             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30532             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30533         }
30534     }
30535     
30536 });
30537
30538  
30539
30540  /*
30541  * Based on:
30542  * Ext JS Library 1.1.1
30543  * Copyright(c) 2006-2007, Ext JS, LLC.
30544  *
30545  * Originally Released Under LGPL - original licence link has changed is not relivant.
30546  *
30547  * Fork - LGPL
30548  * <script type="text/javascript">
30549  */
30550
30551
30552 /**
30553  * @class Roo.bootstrap.SplitBar
30554  * @extends Roo.util.Observable
30555  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30556  * <br><br>
30557  * Usage:
30558  * <pre><code>
30559 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30560                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30561 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30562 split.minSize = 100;
30563 split.maxSize = 600;
30564 split.animate = true;
30565 split.on('moved', splitterMoved);
30566 </code></pre>
30567  * @constructor
30568  * Create a new SplitBar
30569  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30570  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30571  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30572  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30573                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30574                         position of the SplitBar).
30575  */
30576 Roo.bootstrap.SplitBar = function(cfg){
30577     
30578     /** @private */
30579     
30580     //{
30581     //  dragElement : elm
30582     //  resizingElement: el,
30583         // optional..
30584     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30585     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30586         // existingProxy ???
30587     //}
30588     
30589     this.el = Roo.get(cfg.dragElement, true);
30590     this.el.dom.unselectable = "on";
30591     /** @private */
30592     this.resizingEl = Roo.get(cfg.resizingElement, true);
30593
30594     /**
30595      * @private
30596      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30597      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30598      * @type Number
30599      */
30600     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30601     
30602     /**
30603      * The minimum size of the resizing element. (Defaults to 0)
30604      * @type Number
30605      */
30606     this.minSize = 0;
30607     
30608     /**
30609      * The maximum size of the resizing element. (Defaults to 2000)
30610      * @type Number
30611      */
30612     this.maxSize = 2000;
30613     
30614     /**
30615      * Whether to animate the transition to the new size
30616      * @type Boolean
30617      */
30618     this.animate = false;
30619     
30620     /**
30621      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30622      * @type Boolean
30623      */
30624     this.useShim = false;
30625     
30626     /** @private */
30627     this.shim = null;
30628     
30629     if(!cfg.existingProxy){
30630         /** @private */
30631         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30632     }else{
30633         this.proxy = Roo.get(cfg.existingProxy).dom;
30634     }
30635     /** @private */
30636     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30637     
30638     /** @private */
30639     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30640     
30641     /** @private */
30642     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30643     
30644     /** @private */
30645     this.dragSpecs = {};
30646     
30647     /**
30648      * @private The adapter to use to positon and resize elements
30649      */
30650     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30651     this.adapter.init(this);
30652     
30653     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30654         /** @private */
30655         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30656         this.el.addClass("roo-splitbar-h");
30657     }else{
30658         /** @private */
30659         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30660         this.el.addClass("roo-splitbar-v");
30661     }
30662     
30663     this.addEvents({
30664         /**
30665          * @event resize
30666          * Fires when the splitter is moved (alias for {@link #event-moved})
30667          * @param {Roo.bootstrap.SplitBar} this
30668          * @param {Number} newSize the new width or height
30669          */
30670         "resize" : true,
30671         /**
30672          * @event moved
30673          * Fires when the splitter is moved
30674          * @param {Roo.bootstrap.SplitBar} this
30675          * @param {Number} newSize the new width or height
30676          */
30677         "moved" : true,
30678         /**
30679          * @event beforeresize
30680          * Fires before the splitter is dragged
30681          * @param {Roo.bootstrap.SplitBar} this
30682          */
30683         "beforeresize" : true,
30684
30685         "beforeapply" : true
30686     });
30687
30688     Roo.util.Observable.call(this);
30689 };
30690
30691 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30692     onStartProxyDrag : function(x, y){
30693         this.fireEvent("beforeresize", this);
30694         if(!this.overlay){
30695             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30696             o.unselectable();
30697             o.enableDisplayMode("block");
30698             // all splitbars share the same overlay
30699             Roo.bootstrap.SplitBar.prototype.overlay = o;
30700         }
30701         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30702         this.overlay.show();
30703         Roo.get(this.proxy).setDisplayed("block");
30704         var size = this.adapter.getElementSize(this);
30705         this.activeMinSize = this.getMinimumSize();;
30706         this.activeMaxSize = this.getMaximumSize();;
30707         var c1 = size - this.activeMinSize;
30708         var c2 = Math.max(this.activeMaxSize - size, 0);
30709         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30710             this.dd.resetConstraints();
30711             this.dd.setXConstraint(
30712                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30713                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30714             );
30715             this.dd.setYConstraint(0, 0);
30716         }else{
30717             this.dd.resetConstraints();
30718             this.dd.setXConstraint(0, 0);
30719             this.dd.setYConstraint(
30720                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30721                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30722             );
30723          }
30724         this.dragSpecs.startSize = size;
30725         this.dragSpecs.startPoint = [x, y];
30726         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30727     },
30728     
30729     /** 
30730      * @private Called after the drag operation by the DDProxy
30731      */
30732     onEndProxyDrag : function(e){
30733         Roo.get(this.proxy).setDisplayed(false);
30734         var endPoint = Roo.lib.Event.getXY(e);
30735         if(this.overlay){
30736             this.overlay.hide();
30737         }
30738         var newSize;
30739         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30740             newSize = this.dragSpecs.startSize + 
30741                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30742                     endPoint[0] - this.dragSpecs.startPoint[0] :
30743                     this.dragSpecs.startPoint[0] - endPoint[0]
30744                 );
30745         }else{
30746             newSize = this.dragSpecs.startSize + 
30747                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30748                     endPoint[1] - this.dragSpecs.startPoint[1] :
30749                     this.dragSpecs.startPoint[1] - endPoint[1]
30750                 );
30751         }
30752         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30753         if(newSize != this.dragSpecs.startSize){
30754             if(this.fireEvent('beforeapply', this, newSize) !== false){
30755                 this.adapter.setElementSize(this, newSize);
30756                 this.fireEvent("moved", this, newSize);
30757                 this.fireEvent("resize", this, newSize);
30758             }
30759         }
30760     },
30761     
30762     /**
30763      * Get the adapter this SplitBar uses
30764      * @return The adapter object
30765      */
30766     getAdapter : function(){
30767         return this.adapter;
30768     },
30769     
30770     /**
30771      * Set the adapter this SplitBar uses
30772      * @param {Object} adapter A SplitBar adapter object
30773      */
30774     setAdapter : function(adapter){
30775         this.adapter = adapter;
30776         this.adapter.init(this);
30777     },
30778     
30779     /**
30780      * Gets the minimum size for the resizing element
30781      * @return {Number} The minimum size
30782      */
30783     getMinimumSize : function(){
30784         return this.minSize;
30785     },
30786     
30787     /**
30788      * Sets the minimum size for the resizing element
30789      * @param {Number} minSize The minimum size
30790      */
30791     setMinimumSize : function(minSize){
30792         this.minSize = minSize;
30793     },
30794     
30795     /**
30796      * Gets the maximum size for the resizing element
30797      * @return {Number} The maximum size
30798      */
30799     getMaximumSize : function(){
30800         return this.maxSize;
30801     },
30802     
30803     /**
30804      * Sets the maximum size for the resizing element
30805      * @param {Number} maxSize The maximum size
30806      */
30807     setMaximumSize : function(maxSize){
30808         this.maxSize = maxSize;
30809     },
30810     
30811     /**
30812      * Sets the initialize size for the resizing element
30813      * @param {Number} size The initial size
30814      */
30815     setCurrentSize : function(size){
30816         var oldAnimate = this.animate;
30817         this.animate = false;
30818         this.adapter.setElementSize(this, size);
30819         this.animate = oldAnimate;
30820     },
30821     
30822     /**
30823      * Destroy this splitbar. 
30824      * @param {Boolean} removeEl True to remove the element
30825      */
30826     destroy : function(removeEl){
30827         if(this.shim){
30828             this.shim.remove();
30829         }
30830         this.dd.unreg();
30831         this.proxy.parentNode.removeChild(this.proxy);
30832         if(removeEl){
30833             this.el.remove();
30834         }
30835     }
30836 });
30837
30838 /**
30839  * @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.
30840  */
30841 Roo.bootstrap.SplitBar.createProxy = function(dir){
30842     var proxy = new Roo.Element(document.createElement("div"));
30843     proxy.unselectable();
30844     var cls = 'roo-splitbar-proxy';
30845     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30846     document.body.appendChild(proxy.dom);
30847     return proxy.dom;
30848 };
30849
30850 /** 
30851  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30852  * Default Adapter. It assumes the splitter and resizing element are not positioned
30853  * elements and only gets/sets the width of the element. Generally used for table based layouts.
30854  */
30855 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30856 };
30857
30858 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30859     // do nothing for now
30860     init : function(s){
30861     
30862     },
30863     /**
30864      * Called before drag operations to get the current size of the resizing element. 
30865      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30866      */
30867      getElementSize : function(s){
30868         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30869             return s.resizingEl.getWidth();
30870         }else{
30871             return s.resizingEl.getHeight();
30872         }
30873     },
30874     
30875     /**
30876      * Called after drag operations to set the size of the resizing element.
30877      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30878      * @param {Number} newSize The new size to set
30879      * @param {Function} onComplete A function to be invoked when resizing is complete
30880      */
30881     setElementSize : function(s, newSize, onComplete){
30882         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30883             if(!s.animate){
30884                 s.resizingEl.setWidth(newSize);
30885                 if(onComplete){
30886                     onComplete(s, newSize);
30887                 }
30888             }else{
30889                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30890             }
30891         }else{
30892             
30893             if(!s.animate){
30894                 s.resizingEl.setHeight(newSize);
30895                 if(onComplete){
30896                     onComplete(s, newSize);
30897                 }
30898             }else{
30899                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30900             }
30901         }
30902     }
30903 };
30904
30905 /** 
30906  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
30907  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
30908  * Adapter that  moves the splitter element to align with the resized sizing element. 
30909  * Used with an absolute positioned SplitBar.
30910  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30911  * document.body, make sure you assign an id to the body element.
30912  */
30913 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
30914     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30915     this.container = Roo.get(container);
30916 };
30917
30918 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
30919     init : function(s){
30920         this.basic.init(s);
30921     },
30922     
30923     getElementSize : function(s){
30924         return this.basic.getElementSize(s);
30925     },
30926     
30927     setElementSize : function(s, newSize, onComplete){
30928         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
30929     },
30930     
30931     moveSplitter : function(s){
30932         var yes = Roo.bootstrap.SplitBar;
30933         switch(s.placement){
30934             case yes.LEFT:
30935                 s.el.setX(s.resizingEl.getRight());
30936                 break;
30937             case yes.RIGHT:
30938                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
30939                 break;
30940             case yes.TOP:
30941                 s.el.setY(s.resizingEl.getBottom());
30942                 break;
30943             case yes.BOTTOM:
30944                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
30945                 break;
30946         }
30947     }
30948 };
30949
30950 /**
30951  * Orientation constant - Create a vertical SplitBar
30952  * @static
30953  * @type Number
30954  */
30955 Roo.bootstrap.SplitBar.VERTICAL = 1;
30956
30957 /**
30958  * Orientation constant - Create a horizontal SplitBar
30959  * @static
30960  * @type Number
30961  */
30962 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
30963
30964 /**
30965  * Placement constant - The resizing element is to the left of the splitter element
30966  * @static
30967  * @type Number
30968  */
30969 Roo.bootstrap.SplitBar.LEFT = 1;
30970
30971 /**
30972  * Placement constant - The resizing element is to the right of the splitter element
30973  * @static
30974  * @type Number
30975  */
30976 Roo.bootstrap.SplitBar.RIGHT = 2;
30977
30978 /**
30979  * Placement constant - The resizing element is positioned above the splitter element
30980  * @static
30981  * @type Number
30982  */
30983 Roo.bootstrap.SplitBar.TOP = 3;
30984
30985 /**
30986  * Placement constant - The resizing element is positioned under splitter element
30987  * @static
30988  * @type Number
30989  */
30990 Roo.bootstrap.SplitBar.BOTTOM = 4;
30991 Roo.namespace("Roo.bootstrap.layout");/*
30992  * Based on:
30993  * Ext JS Library 1.1.1
30994  * Copyright(c) 2006-2007, Ext JS, LLC.
30995  *
30996  * Originally Released Under LGPL - original licence link has changed is not relivant.
30997  *
30998  * Fork - LGPL
30999  * <script type="text/javascript">
31000  */
31001  
31002 /**
31003  * @class Roo.bootstrap.layout.Manager
31004  * @extends Roo.bootstrap.Component
31005  * Base class for layout managers.
31006  */
31007 Roo.bootstrap.layout.Manager = function(config)
31008 {
31009     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31010     this.el = Roo.get(config.el);
31011     // ie scrollbar fix
31012     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31013         document.body.scroll = "no";
31014     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31015         this.el.position('relative');
31016     }
31017     
31018     this.id = this.el.id;
31019     this.el.addClass("roo-layout-container");
31020     /** false to disable window resize monitoring @type Boolean */
31021     this.monitorWindowResize = true;
31022     this.regions = {};
31023     this.addEvents({
31024         /**
31025          * @event layout
31026          * Fires when a layout is performed. 
31027          * @param {Roo.LayoutManager} this
31028          */
31029         "layout" : true,
31030         /**
31031          * @event regionresized
31032          * Fires when the user resizes a region. 
31033          * @param {Roo.LayoutRegion} region The resized region
31034          * @param {Number} newSize The new size (width for east/west, height for north/south)
31035          */
31036         "regionresized" : true,
31037         /**
31038          * @event regioncollapsed
31039          * Fires when a region is collapsed. 
31040          * @param {Roo.LayoutRegion} region The collapsed region
31041          */
31042         "regioncollapsed" : true,
31043         /**
31044          * @event regionexpanded
31045          * Fires when a region is expanded.  
31046          * @param {Roo.LayoutRegion} region The expanded region
31047          */
31048         "regionexpanded" : true
31049     });
31050     this.updating = false;
31051     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31052 };
31053
31054 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31055     
31056     
31057     regions : null,
31058     
31059     monitorWindowResize : true,
31060     
31061     
31062     updating : false,
31063     
31064     /**
31065      * Returns true if this layout is currently being updated
31066      * @return {Boolean}
31067      */
31068     isUpdating : function(){
31069         return this.updating; 
31070     },
31071     
31072     /**
31073      * Suspend the LayoutManager from doing auto-layouts while
31074      * making multiple add or remove calls
31075      */
31076     beginUpdate : function(){
31077         this.updating = true;    
31078     },
31079     
31080     /**
31081      * Restore auto-layouts and optionally disable the manager from performing a layout
31082      * @param {Boolean} noLayout true to disable a layout update 
31083      */
31084     endUpdate : function(noLayout){
31085         this.updating = false;
31086         if(!noLayout){
31087             this.layout();
31088         }    
31089     },
31090     
31091     layout: function(){
31092         // abstract...
31093     },
31094     
31095     onRegionResized : function(region, newSize){
31096         this.fireEvent("regionresized", region, newSize);
31097         this.layout();
31098     },
31099     
31100     onRegionCollapsed : function(region){
31101         this.fireEvent("regioncollapsed", region);
31102     },
31103     
31104     onRegionExpanded : function(region){
31105         this.fireEvent("regionexpanded", region);
31106     },
31107         
31108     /**
31109      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31110      * performs box-model adjustments.
31111      * @return {Object} The size as an object {width: (the width), height: (the height)}
31112      */
31113     getViewSize : function()
31114     {
31115         var size;
31116         if(this.el.dom != document.body){
31117             size = this.el.getSize();
31118         }else{
31119             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31120         }
31121         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31122         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31123         return size;
31124     },
31125     
31126     /**
31127      * Returns the Element this layout is bound to.
31128      * @return {Roo.Element}
31129      */
31130     getEl : function(){
31131         return this.el;
31132     },
31133     
31134     /**
31135      * Returns the specified region.
31136      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31137      * @return {Roo.LayoutRegion}
31138      */
31139     getRegion : function(target){
31140         return this.regions[target.toLowerCase()];
31141     },
31142     
31143     onWindowResize : function(){
31144         if(this.monitorWindowResize){
31145             this.layout();
31146         }
31147     }
31148 });/*
31149  * Based on:
31150  * Ext JS Library 1.1.1
31151  * Copyright(c) 2006-2007, Ext JS, LLC.
31152  *
31153  * Originally Released Under LGPL - original licence link has changed is not relivant.
31154  *
31155  * Fork - LGPL
31156  * <script type="text/javascript">
31157  */
31158 /**
31159  * @class Roo.bootstrap.layout.Border
31160  * @extends Roo.bootstrap.layout.Manager
31161  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31162  * please see: examples/bootstrap/nested.html<br><br>
31163  
31164 <b>The container the layout is rendered into can be either the body element or any other element.
31165 If it is not the body element, the container needs to either be an absolute positioned element,
31166 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31167 the container size if it is not the body element.</b>
31168
31169 * @constructor
31170 * Create a new Border
31171 * @param {Object} config Configuration options
31172  */
31173 Roo.bootstrap.layout.Border = function(config){
31174     config = config || {};
31175     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31176     
31177     
31178     
31179     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31180         if(config[region]){
31181             config[region].region = region;
31182             this.addRegion(config[region]);
31183         }
31184     },this);
31185     
31186 };
31187
31188 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31189
31190 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31191     /**
31192      * Creates and adds a new region if it doesn't already exist.
31193      * @param {String} target The target region key (north, south, east, west or center).
31194      * @param {Object} config The regions config object
31195      * @return {BorderLayoutRegion} The new region
31196      */
31197     addRegion : function(config)
31198     {
31199         if(!this.regions[config.region]){
31200             var r = this.factory(config);
31201             this.bindRegion(r);
31202         }
31203         return this.regions[config.region];
31204     },
31205
31206     // private (kinda)
31207     bindRegion : function(r){
31208         this.regions[r.config.region] = r;
31209         
31210         r.on("visibilitychange",    this.layout, this);
31211         r.on("paneladded",          this.layout, this);
31212         r.on("panelremoved",        this.layout, this);
31213         r.on("invalidated",         this.layout, this);
31214         r.on("resized",             this.onRegionResized, this);
31215         r.on("collapsed",           this.onRegionCollapsed, this);
31216         r.on("expanded",            this.onRegionExpanded, this);
31217     },
31218
31219     /**
31220      * Performs a layout update.
31221      */
31222     layout : function()
31223     {
31224         if(this.updating) {
31225             return;
31226         }
31227         var size = this.getViewSize();
31228         var w = size.width;
31229         var h = size.height;
31230         var centerW = w;
31231         var centerH = h;
31232         var centerY = 0;
31233         var centerX = 0;
31234         //var x = 0, y = 0;
31235
31236         var rs = this.regions;
31237         var north = rs["north"];
31238         var south = rs["south"]; 
31239         var west = rs["west"];
31240         var east = rs["east"];
31241         var center = rs["center"];
31242         //if(this.hideOnLayout){ // not supported anymore
31243             //c.el.setStyle("display", "none");
31244         //}
31245         if(north && north.isVisible()){
31246             var b = north.getBox();
31247             var m = north.getMargins();
31248             b.width = w - (m.left+m.right);
31249             b.x = m.left;
31250             b.y = m.top;
31251             centerY = b.height + b.y + m.bottom;
31252             centerH -= centerY;
31253             north.updateBox(this.safeBox(b));
31254         }
31255         if(south && south.isVisible()){
31256             var b = south.getBox();
31257             var m = south.getMargins();
31258             b.width = w - (m.left+m.right);
31259             b.x = m.left;
31260             var totalHeight = (b.height + m.top + m.bottom);
31261             b.y = h - totalHeight + m.top;
31262             centerH -= totalHeight;
31263             south.updateBox(this.safeBox(b));
31264         }
31265         if(west && west.isVisible()){
31266             var b = west.getBox();
31267             var m = west.getMargins();
31268             b.height = centerH - (m.top+m.bottom);
31269             b.x = m.left;
31270             b.y = centerY + m.top;
31271             var totalWidth = (b.width + m.left + m.right);
31272             centerX += totalWidth;
31273             centerW -= totalWidth;
31274             west.updateBox(this.safeBox(b));
31275         }
31276         if(east && east.isVisible()){
31277             var b = east.getBox();
31278             var m = east.getMargins();
31279             b.height = centerH - (m.top+m.bottom);
31280             var totalWidth = (b.width + m.left + m.right);
31281             b.x = w - totalWidth + m.left;
31282             b.y = centerY + m.top;
31283             centerW -= totalWidth;
31284             east.updateBox(this.safeBox(b));
31285         }
31286         if(center){
31287             var m = center.getMargins();
31288             var centerBox = {
31289                 x: centerX + m.left,
31290                 y: centerY + m.top,
31291                 width: centerW - (m.left+m.right),
31292                 height: centerH - (m.top+m.bottom)
31293             };
31294             //if(this.hideOnLayout){
31295                 //center.el.setStyle("display", "block");
31296             //}
31297             center.updateBox(this.safeBox(centerBox));
31298         }
31299         this.el.repaint();
31300         this.fireEvent("layout", this);
31301     },
31302
31303     // private
31304     safeBox : function(box){
31305         box.width = Math.max(0, box.width);
31306         box.height = Math.max(0, box.height);
31307         return box;
31308     },
31309
31310     /**
31311      * Adds a ContentPanel (or subclass) to this layout.
31312      * @param {String} target The target region key (north, south, east, west or center).
31313      * @param {Roo.ContentPanel} panel The panel to add
31314      * @return {Roo.ContentPanel} The added panel
31315      */
31316     add : function(target, panel){
31317          
31318         target = target.toLowerCase();
31319         return this.regions[target].add(panel);
31320     },
31321
31322     /**
31323      * Remove a ContentPanel (or subclass) to this layout.
31324      * @param {String} target The target region key (north, south, east, west or center).
31325      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31326      * @return {Roo.ContentPanel} The removed panel
31327      */
31328     remove : function(target, panel){
31329         target = target.toLowerCase();
31330         return this.regions[target].remove(panel);
31331     },
31332
31333     /**
31334      * Searches all regions for a panel with the specified id
31335      * @param {String} panelId
31336      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31337      */
31338     findPanel : function(panelId){
31339         var rs = this.regions;
31340         for(var target in rs){
31341             if(typeof rs[target] != "function"){
31342                 var p = rs[target].getPanel(panelId);
31343                 if(p){
31344                     return p;
31345                 }
31346             }
31347         }
31348         return null;
31349     },
31350
31351     /**
31352      * Searches all regions for a panel with the specified id and activates (shows) it.
31353      * @param {String/ContentPanel} panelId The panels id or the panel itself
31354      * @return {Roo.ContentPanel} The shown panel or null
31355      */
31356     showPanel : function(panelId) {
31357       var rs = this.regions;
31358       for(var target in rs){
31359          var r = rs[target];
31360          if(typeof r != "function"){
31361             if(r.hasPanel(panelId)){
31362                return r.showPanel(panelId);
31363             }
31364          }
31365       }
31366       return null;
31367    },
31368
31369    /**
31370      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31371      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31372      */
31373    /*
31374     restoreState : function(provider){
31375         if(!provider){
31376             provider = Roo.state.Manager;
31377         }
31378         var sm = new Roo.LayoutStateManager();
31379         sm.init(this, provider);
31380     },
31381 */
31382  
31383  
31384     /**
31385      * Adds a xtype elements to the layout.
31386      * <pre><code>
31387
31388 layout.addxtype({
31389        xtype : 'ContentPanel',
31390        region: 'west',
31391        items: [ .... ]
31392    }
31393 );
31394
31395 layout.addxtype({
31396         xtype : 'NestedLayoutPanel',
31397         region: 'west',
31398         layout: {
31399            center: { },
31400            west: { }   
31401         },
31402         items : [ ... list of content panels or nested layout panels.. ]
31403    }
31404 );
31405 </code></pre>
31406      * @param {Object} cfg Xtype definition of item to add.
31407      */
31408     addxtype : function(cfg)
31409     {
31410         // basically accepts a pannel...
31411         // can accept a layout region..!?!?
31412         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31413         
31414         
31415         // theory?  children can only be panels??
31416         
31417         //if (!cfg.xtype.match(/Panel$/)) {
31418         //    return false;
31419         //}
31420         var ret = false;
31421         
31422         if (typeof(cfg.region) == 'undefined') {
31423             Roo.log("Failed to add Panel, region was not set");
31424             Roo.log(cfg);
31425             return false;
31426         }
31427         var region = cfg.region;
31428         delete cfg.region;
31429         
31430           
31431         var xitems = [];
31432         if (cfg.items) {
31433             xitems = cfg.items;
31434             delete cfg.items;
31435         }
31436         var nb = false;
31437         
31438         switch(cfg.xtype) 
31439         {
31440             case 'Content':  // ContentPanel (el, cfg)
31441             case 'Scroll':  // ContentPanel (el, cfg)
31442             case 'View': 
31443                 cfg.autoCreate = true;
31444                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31445                 //} else {
31446                 //    var el = this.el.createChild();
31447                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31448                 //}
31449                 
31450                 this.add(region, ret);
31451                 break;
31452             
31453             /*
31454             case 'TreePanel': // our new panel!
31455                 cfg.el = this.el.createChild();
31456                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31457                 this.add(region, ret);
31458                 break;
31459             */
31460             
31461             case 'Nest': 
31462                 // create a new Layout (which is  a Border Layout...
31463                 
31464                 var clayout = cfg.layout;
31465                 clayout.el  = this.el.createChild();
31466                 clayout.items   = clayout.items  || [];
31467                 
31468                 delete cfg.layout;
31469                 
31470                 // replace this exitems with the clayout ones..
31471                 xitems = clayout.items;
31472                  
31473                 // force background off if it's in center...
31474                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31475                     cfg.background = false;
31476                 }
31477                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31478                 
31479                 
31480                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31481                 //console.log('adding nested layout panel '  + cfg.toSource());
31482                 this.add(region, ret);
31483                 nb = {}; /// find first...
31484                 break;
31485             
31486             case 'Grid':
31487                 
31488                 // needs grid and region
31489                 
31490                 //var el = this.getRegion(region).el.createChild();
31491                 var el = this.el.createChild();
31492                 // create the grid first...
31493                 cfg.grid.container = el;
31494                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31495                 
31496                 
31497                 if (region == 'center' && this.active ) {
31498                     cfg.background = false;
31499                 }
31500                 
31501                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31502                 
31503                 this.add(region, ret);
31504                 
31505                 if (cfg.background) {
31506                     // render grid on panel activation (if panel background)
31507                     ret.on('activate', function(gp) {
31508                         if (!gp.grid.rendered) {
31509                             gp.grid.render(gp.grid.getGridEl());
31510                         }
31511                     });
31512                 } else {
31513                     cfg.grid.render(cfg.grid.getGridEl());
31514                 }
31515                 break;
31516            
31517            
31518             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31519                 // it was the old xcomponent building that caused this before.
31520                 // espeically if border is the top element in the tree.
31521                 ret = this;
31522                 break; 
31523                 
31524                     
31525                 
31526                 
31527                 
31528             default:
31529                 /*
31530                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31531                     
31532                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31533                     this.add(region, ret);
31534                 } else {
31535                 */
31536                     Roo.log(cfg);
31537                     throw "Can not add '" + cfg.xtype + "' to Border";
31538                     return null;
31539              
31540                                 
31541              
31542         }
31543         this.beginUpdate();
31544         // add children..
31545         var region = '';
31546         var abn = {};
31547         Roo.each(xitems, function(i)  {
31548             region = nb && i.region ? i.region : false;
31549             
31550             var add = ret.addxtype(i);
31551            
31552             if (region) {
31553                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31554                 if (!i.background) {
31555                     abn[region] = nb[region] ;
31556                 }
31557             }
31558             
31559         });
31560         this.endUpdate();
31561
31562         // make the last non-background panel active..
31563         //if (nb) { Roo.log(abn); }
31564         if (nb) {
31565             
31566             for(var r in abn) {
31567                 region = this.getRegion(r);
31568                 if (region) {
31569                     // tried using nb[r], but it does not work..
31570                      
31571                     region.showPanel(abn[r]);
31572                    
31573                 }
31574             }
31575         }
31576         return ret;
31577         
31578     },
31579     
31580     
31581 // private
31582     factory : function(cfg)
31583     {
31584         
31585         var validRegions = Roo.bootstrap.layout.Border.regions;
31586
31587         var target = cfg.region;
31588         cfg.mgr = this;
31589         
31590         var r = Roo.bootstrap.layout;
31591         Roo.log(target);
31592         switch(target){
31593             case "north":
31594                 return new r.North(cfg);
31595             case "south":
31596                 return new r.South(cfg);
31597             case "east":
31598                 return new r.East(cfg);
31599             case "west":
31600                 return new r.West(cfg);
31601             case "center":
31602                 return new r.Center(cfg);
31603         }
31604         throw 'Layout region "'+target+'" not supported.';
31605     }
31606     
31607     
31608 });
31609  /*
31610  * Based on:
31611  * Ext JS Library 1.1.1
31612  * Copyright(c) 2006-2007, Ext JS, LLC.
31613  *
31614  * Originally Released Under LGPL - original licence link has changed is not relivant.
31615  *
31616  * Fork - LGPL
31617  * <script type="text/javascript">
31618  */
31619  
31620 /**
31621  * @class Roo.bootstrap.layout.Basic
31622  * @extends Roo.util.Observable
31623  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31624  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31625  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31626  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31627  * @cfg {string}   region  the region that it inhabits..
31628  * @cfg {bool}   skipConfig skip config?
31629  * 
31630
31631  */
31632 Roo.bootstrap.layout.Basic = function(config){
31633     
31634     this.mgr = config.mgr;
31635     
31636     this.position = config.region;
31637     
31638     var skipConfig = config.skipConfig;
31639     
31640     this.events = {
31641         /**
31642          * @scope Roo.BasicLayoutRegion
31643          */
31644         
31645         /**
31646          * @event beforeremove
31647          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31648          * @param {Roo.LayoutRegion} this
31649          * @param {Roo.ContentPanel} panel The panel
31650          * @param {Object} e The cancel event object
31651          */
31652         "beforeremove" : true,
31653         /**
31654          * @event invalidated
31655          * Fires when the layout for this region is changed.
31656          * @param {Roo.LayoutRegion} this
31657          */
31658         "invalidated" : true,
31659         /**
31660          * @event visibilitychange
31661          * Fires when this region is shown or hidden 
31662          * @param {Roo.LayoutRegion} this
31663          * @param {Boolean} visibility true or false
31664          */
31665         "visibilitychange" : true,
31666         /**
31667          * @event paneladded
31668          * Fires when a panel is added. 
31669          * @param {Roo.LayoutRegion} this
31670          * @param {Roo.ContentPanel} panel The panel
31671          */
31672         "paneladded" : true,
31673         /**
31674          * @event panelremoved
31675          * Fires when a panel is removed. 
31676          * @param {Roo.LayoutRegion} this
31677          * @param {Roo.ContentPanel} panel The panel
31678          */
31679         "panelremoved" : true,
31680         /**
31681          * @event beforecollapse
31682          * Fires when this region before collapse.
31683          * @param {Roo.LayoutRegion} this
31684          */
31685         "beforecollapse" : true,
31686         /**
31687          * @event collapsed
31688          * Fires when this region is collapsed.
31689          * @param {Roo.LayoutRegion} this
31690          */
31691         "collapsed" : true,
31692         /**
31693          * @event expanded
31694          * Fires when this region is expanded.
31695          * @param {Roo.LayoutRegion} this
31696          */
31697         "expanded" : true,
31698         /**
31699          * @event slideshow
31700          * Fires when this region is slid into view.
31701          * @param {Roo.LayoutRegion} this
31702          */
31703         "slideshow" : true,
31704         /**
31705          * @event slidehide
31706          * Fires when this region slides out of view. 
31707          * @param {Roo.LayoutRegion} this
31708          */
31709         "slidehide" : true,
31710         /**
31711          * @event panelactivated
31712          * Fires when a panel is activated. 
31713          * @param {Roo.LayoutRegion} this
31714          * @param {Roo.ContentPanel} panel The activated panel
31715          */
31716         "panelactivated" : true,
31717         /**
31718          * @event resized
31719          * Fires when the user resizes this region. 
31720          * @param {Roo.LayoutRegion} this
31721          * @param {Number} newSize The new size (width for east/west, height for north/south)
31722          */
31723         "resized" : true
31724     };
31725     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31726     this.panels = new Roo.util.MixedCollection();
31727     this.panels.getKey = this.getPanelId.createDelegate(this);
31728     this.box = null;
31729     this.activePanel = null;
31730     // ensure listeners are added...
31731     
31732     if (config.listeners || config.events) {
31733         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31734             listeners : config.listeners || {},
31735             events : config.events || {}
31736         });
31737     }
31738     
31739     if(skipConfig !== true){
31740         this.applyConfig(config);
31741     }
31742 };
31743
31744 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31745 {
31746     getPanelId : function(p){
31747         return p.getId();
31748     },
31749     
31750     applyConfig : function(config){
31751         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31752         this.config = config;
31753         
31754     },
31755     
31756     /**
31757      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31758      * the width, for horizontal (north, south) the height.
31759      * @param {Number} newSize The new width or height
31760      */
31761     resizeTo : function(newSize){
31762         var el = this.el ? this.el :
31763                  (this.activePanel ? this.activePanel.getEl() : null);
31764         if(el){
31765             switch(this.position){
31766                 case "east":
31767                 case "west":
31768                     el.setWidth(newSize);
31769                     this.fireEvent("resized", this, newSize);
31770                 break;
31771                 case "north":
31772                 case "south":
31773                     el.setHeight(newSize);
31774                     this.fireEvent("resized", this, newSize);
31775                 break;                
31776             }
31777         }
31778     },
31779     
31780     getBox : function(){
31781         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31782     },
31783     
31784     getMargins : function(){
31785         return this.margins;
31786     },
31787     
31788     updateBox : function(box){
31789         this.box = box;
31790         var el = this.activePanel.getEl();
31791         el.dom.style.left = box.x + "px";
31792         el.dom.style.top = box.y + "px";
31793         this.activePanel.setSize(box.width, box.height);
31794     },
31795     
31796     /**
31797      * Returns the container element for this region.
31798      * @return {Roo.Element}
31799      */
31800     getEl : function(){
31801         return this.activePanel;
31802     },
31803     
31804     /**
31805      * Returns true if this region is currently visible.
31806      * @return {Boolean}
31807      */
31808     isVisible : function(){
31809         return this.activePanel ? true : false;
31810     },
31811     
31812     setActivePanel : function(panel){
31813         panel = this.getPanel(panel);
31814         if(this.activePanel && this.activePanel != panel){
31815             this.activePanel.setActiveState(false);
31816             this.activePanel.getEl().setLeftTop(-10000,-10000);
31817         }
31818         this.activePanel = panel;
31819         panel.setActiveState(true);
31820         if(this.box){
31821             panel.setSize(this.box.width, this.box.height);
31822         }
31823         this.fireEvent("panelactivated", this, panel);
31824         this.fireEvent("invalidated");
31825     },
31826     
31827     /**
31828      * Show the specified panel.
31829      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31830      * @return {Roo.ContentPanel} The shown panel or null
31831      */
31832     showPanel : function(panel){
31833         panel = this.getPanel(panel);
31834         if(panel){
31835             this.setActivePanel(panel);
31836         }
31837         return panel;
31838     },
31839     
31840     /**
31841      * Get the active panel for this region.
31842      * @return {Roo.ContentPanel} The active panel or null
31843      */
31844     getActivePanel : function(){
31845         return this.activePanel;
31846     },
31847     
31848     /**
31849      * Add the passed ContentPanel(s)
31850      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31851      * @return {Roo.ContentPanel} The panel added (if only one was added)
31852      */
31853     add : function(panel){
31854         if(arguments.length > 1){
31855             for(var i = 0, len = arguments.length; i < len; i++) {
31856                 this.add(arguments[i]);
31857             }
31858             return null;
31859         }
31860         if(this.hasPanel(panel)){
31861             this.showPanel(panel);
31862             return panel;
31863         }
31864         var el = panel.getEl();
31865         if(el.dom.parentNode != this.mgr.el.dom){
31866             this.mgr.el.dom.appendChild(el.dom);
31867         }
31868         if(panel.setRegion){
31869             panel.setRegion(this);
31870         }
31871         this.panels.add(panel);
31872         el.setStyle("position", "absolute");
31873         if(!panel.background){
31874             this.setActivePanel(panel);
31875             if(this.config.initialSize && this.panels.getCount()==1){
31876                 this.resizeTo(this.config.initialSize);
31877             }
31878         }
31879         this.fireEvent("paneladded", this, panel);
31880         return panel;
31881     },
31882     
31883     /**
31884      * Returns true if the panel is in this region.
31885      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31886      * @return {Boolean}
31887      */
31888     hasPanel : function(panel){
31889         if(typeof panel == "object"){ // must be panel obj
31890             panel = panel.getId();
31891         }
31892         return this.getPanel(panel) ? true : false;
31893     },
31894     
31895     /**
31896      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31897      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31898      * @param {Boolean} preservePanel Overrides the config preservePanel option
31899      * @return {Roo.ContentPanel} The panel that was removed
31900      */
31901     remove : function(panel, preservePanel){
31902         panel = this.getPanel(panel);
31903         if(!panel){
31904             return null;
31905         }
31906         var e = {};
31907         this.fireEvent("beforeremove", this, panel, e);
31908         if(e.cancel === true){
31909             return null;
31910         }
31911         var panelId = panel.getId();
31912         this.panels.removeKey(panelId);
31913         return panel;
31914     },
31915     
31916     /**
31917      * Returns the panel specified or null if it's not in this region.
31918      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31919      * @return {Roo.ContentPanel}
31920      */
31921     getPanel : function(id){
31922         if(typeof id == "object"){ // must be panel obj
31923             return id;
31924         }
31925         return this.panels.get(id);
31926     },
31927     
31928     /**
31929      * Returns this regions position (north/south/east/west/center).
31930      * @return {String} 
31931      */
31932     getPosition: function(){
31933         return this.position;    
31934     }
31935 });/*
31936  * Based on:
31937  * Ext JS Library 1.1.1
31938  * Copyright(c) 2006-2007, Ext JS, LLC.
31939  *
31940  * Originally Released Under LGPL - original licence link has changed is not relivant.
31941  *
31942  * Fork - LGPL
31943  * <script type="text/javascript">
31944  */
31945  
31946 /**
31947  * @class Roo.bootstrap.layout.Region
31948  * @extends Roo.bootstrap.layout.Basic
31949  * This class represents a region in a layout manager.
31950  
31951  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31952  * @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})
31953  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
31954  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31955  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31956  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31957  * @cfg {String}    title           The title for the region (overrides panel titles)
31958  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31959  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31960  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31961  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31962  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31963  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31964  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31965  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31966  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31967  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
31968
31969  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31970  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31971  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31972  * @cfg {Number}    width           For East/West panels
31973  * @cfg {Number}    height          For North/South panels
31974  * @cfg {Boolean}   split           To show the splitter
31975  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31976  * 
31977  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31978  * @cfg {string}   region  the region that it inhabits..
31979  *
31980
31981  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
31982  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
31983
31984  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
31985  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
31986  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
31987  */
31988 Roo.bootstrap.layout.Region = function(config)
31989 {
31990     
31991     var mgr = config.mgr;
31992     var pos = config.region;
31993     config.skipConfig = true;
31994     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
31995     var dh = Roo.DomHelper;
31996     /** This region's container element 
31997     * @type Roo.Element */
31998     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "roo-layout-region roo-layout-panel roo-layout-panel-" + this.position}, true);
31999     /** This region's title element 
32000     * @type Roo.Element */
32001
32002     this.titleEl = dh.append(this.el.dom,
32003         {
32004                 tag: "div",
32005                 unselectable: "on",
32006                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32007                 children:[
32008                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32009                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32010                 ]}, true);
32011     
32012     this.titleEl.enableDisplayMode();
32013     /** This region's title text element 
32014     * @type HTMLElement */
32015     this.titleTextEl = this.titleEl.dom.firstChild;
32016     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32017     /*
32018     this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32019     this.closeBtn.enableDisplayMode();
32020     this.closeBtn.on("click", this.closeClicked, this);
32021     this.closeBtn.hide();
32022 */
32023     this.createBody(config);
32024     this.visible = true;
32025     this.collapsed = false;
32026
32027     if(config.hideWhenEmpty){
32028         this.hide();
32029         this.on("paneladded", this.validateVisibility, this);
32030         this.on("panelremoved", this.validateVisibility, this);
32031     }
32032     this.applyConfig(config);
32033 };
32034
32035 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32036
32037
32038
32039     createBody : function(){
32040         /** This region's body element 
32041         * @type Roo.Element */
32042         this.bodyEl = this.el.createChild({
32043                 tag: "div",
32044                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32045         });
32046     },
32047
32048     applyConfig : function(c)
32049     {
32050         /*
32051          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32052             var dh = Roo.DomHelper;
32053             if(c.titlebar !== false){
32054                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32055                 this.collapseBtn.on("click", this.collapse, this);
32056                 this.collapseBtn.enableDisplayMode();
32057                 /*
32058                 if(c.showPin === true || this.showPin){
32059                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32060                     this.stickBtn.enableDisplayMode();
32061                     this.stickBtn.on("click", this.expand, this);
32062                     this.stickBtn.hide();
32063                 }
32064                 
32065             }
32066             */
32067             /** This region's collapsed element
32068             * @type Roo.Element */
32069             /*
32070              *
32071             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32072                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32073             ]}, true);
32074             
32075             if(c.floatable !== false){
32076                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32077                this.collapsedEl.on("click", this.collapseClick, this);
32078             }
32079
32080             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32081                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32082                    id: "message", unselectable: "on", style:{"float":"left"}});
32083                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32084              }
32085             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32086             this.expandBtn.on("click", this.expand, this);
32087             
32088         }
32089         
32090         if(this.collapseBtn){
32091             this.collapseBtn.setVisible(c.collapsible == true);
32092         }
32093         
32094         this.cmargins = c.cmargins || this.cmargins ||
32095                          (this.position == "west" || this.position == "east" ?
32096                              {top: 0, left: 2, right:2, bottom: 0} :
32097                              {top: 2, left: 0, right:0, bottom: 2});
32098         */
32099         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32100         
32101         
32102         this.bottomTabs = c.tabPosition != "top";
32103         
32104         this.autoScroll = c.autoScroll || false;
32105         
32106         
32107         if(this.autoScroll){
32108             this.bodyEl.setStyle("overflow", "auto");
32109         }else{
32110             this.bodyEl.setStyle("overflow", c.overflow || 'hidden');
32111         }
32112         //if(c.titlebar !== false){
32113             if((!c.titlebar && !c.title) || c.titlebar === false){
32114                 this.titleEl.hide();
32115             }else{
32116                 this.titleEl.show();
32117                 if(c.title){
32118                     this.titleTextEl.innerHTML = c.title;
32119                 }
32120             }
32121         //}
32122         this.duration = c.duration || .30;
32123         this.slideDuration = c.slideDuration || .45;
32124         this.config = c;
32125         if(c.collapsed){
32126             this.collapse(true);
32127         }
32128         if(c.hidden){
32129             this.hide();
32130         }
32131     },
32132     /**
32133      * Returns true if this region is currently visible.
32134      * @return {Boolean}
32135      */
32136     isVisible : function(){
32137         return this.visible;
32138     },
32139
32140     /**
32141      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32142      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32143      */
32144     //setCollapsedTitle : function(title){
32145     //    title = title || "&#160;";
32146      //   if(this.collapsedTitleTextEl){
32147       //      this.collapsedTitleTextEl.innerHTML = title;
32148        // }
32149     //},
32150
32151     getBox : function(){
32152         var b;
32153       //  if(!this.collapsed){
32154             b = this.el.getBox(false, true);
32155        // }else{
32156           //  b = this.collapsedEl.getBox(false, true);
32157         //}
32158         return b;
32159     },
32160
32161     getMargins : function(){
32162         return this.margins;
32163         //return this.collapsed ? this.cmargins : this.margins;
32164     },
32165 /*
32166     highlight : function(){
32167         this.el.addClass("x-layout-panel-dragover");
32168     },
32169
32170     unhighlight : function(){
32171         this.el.removeClass("x-layout-panel-dragover");
32172     },
32173 */
32174     updateBox : function(box)
32175     {
32176         this.box = box;
32177         if(!this.collapsed){
32178             this.el.dom.style.left = box.x + "px";
32179             this.el.dom.style.top = box.y + "px";
32180             this.updateBody(box.width, box.height);
32181         }else{
32182             this.collapsedEl.dom.style.left = box.x + "px";
32183             this.collapsedEl.dom.style.top = box.y + "px";
32184             this.collapsedEl.setSize(box.width, box.height);
32185         }
32186         if(this.tabs){
32187             this.tabs.autoSizeTabs();
32188         }
32189     },
32190
32191     updateBody : function(w, h)
32192     {
32193         if(w !== null){
32194             this.el.setWidth(w);
32195             w -= this.el.getBorderWidth("rl");
32196             if(this.config.adjustments){
32197                 w += this.config.adjustments[0];
32198             }
32199         }
32200         if(h !== null){
32201             this.el.setHeight(h);
32202             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32203             h -= this.el.getBorderWidth("tb");
32204             if(this.config.adjustments){
32205                 h += this.config.adjustments[1];
32206             }
32207             this.bodyEl.setHeight(h);
32208             if(this.tabs){
32209                 h = this.tabs.syncHeight(h);
32210             }
32211         }
32212         if(this.panelSize){
32213             w = w !== null ? w : this.panelSize.width;
32214             h = h !== null ? h : this.panelSize.height;
32215         }
32216         if(this.activePanel){
32217             var el = this.activePanel.getEl();
32218             w = w !== null ? w : el.getWidth();
32219             h = h !== null ? h : el.getHeight();
32220             this.panelSize = {width: w, height: h};
32221             this.activePanel.setSize(w, h);
32222         }
32223         if(Roo.isIE && this.tabs){
32224             this.tabs.el.repaint();
32225         }
32226     },
32227
32228     /**
32229      * Returns the container element for this region.
32230      * @return {Roo.Element}
32231      */
32232     getEl : function(){
32233         return this.el;
32234     },
32235
32236     /**
32237      * Hides this region.
32238      */
32239     hide : function(){
32240         //if(!this.collapsed){
32241             this.el.dom.style.left = "-2000px";
32242             this.el.hide();
32243         //}else{
32244          //   this.collapsedEl.dom.style.left = "-2000px";
32245          //   this.collapsedEl.hide();
32246        // }
32247         this.visible = false;
32248         this.fireEvent("visibilitychange", this, false);
32249     },
32250
32251     /**
32252      * Shows this region if it was previously hidden.
32253      */
32254     show : function(){
32255         //if(!this.collapsed){
32256             this.el.show();
32257         //}else{
32258         //    this.collapsedEl.show();
32259        // }
32260         this.visible = true;
32261         this.fireEvent("visibilitychange", this, true);
32262     },
32263 /*
32264     closeClicked : function(){
32265         if(this.activePanel){
32266             this.remove(this.activePanel);
32267         }
32268     },
32269
32270     collapseClick : function(e){
32271         if(this.isSlid){
32272            e.stopPropagation();
32273            this.slideIn();
32274         }else{
32275            e.stopPropagation();
32276            this.slideOut();
32277         }
32278     },
32279 */
32280     /**
32281      * Collapses this region.
32282      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32283      */
32284     /*
32285     collapse : function(skipAnim, skipCheck = false){
32286         if(this.collapsed) {
32287             return;
32288         }
32289         
32290         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32291             
32292             this.collapsed = true;
32293             if(this.split){
32294                 this.split.el.hide();
32295             }
32296             if(this.config.animate && skipAnim !== true){
32297                 this.fireEvent("invalidated", this);
32298                 this.animateCollapse();
32299             }else{
32300                 this.el.setLocation(-20000,-20000);
32301                 this.el.hide();
32302                 this.collapsedEl.show();
32303                 this.fireEvent("collapsed", this);
32304                 this.fireEvent("invalidated", this);
32305             }
32306         }
32307         
32308     },
32309 */
32310     animateCollapse : function(){
32311         // overridden
32312     },
32313
32314     /**
32315      * Expands this region if it was previously collapsed.
32316      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32317      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32318      */
32319     /*
32320     expand : function(e, skipAnim){
32321         if(e) {
32322             e.stopPropagation();
32323         }
32324         if(!this.collapsed || this.el.hasActiveFx()) {
32325             return;
32326         }
32327         if(this.isSlid){
32328             this.afterSlideIn();
32329             skipAnim = true;
32330         }
32331         this.collapsed = false;
32332         if(this.config.animate && skipAnim !== true){
32333             this.animateExpand();
32334         }else{
32335             this.el.show();
32336             if(this.split){
32337                 this.split.el.show();
32338             }
32339             this.collapsedEl.setLocation(-2000,-2000);
32340             this.collapsedEl.hide();
32341             this.fireEvent("invalidated", this);
32342             this.fireEvent("expanded", this);
32343         }
32344     },
32345 */
32346     animateExpand : function(){
32347         // overridden
32348     },
32349
32350     initTabs : function()
32351     {
32352         this.bodyEl.setStyle("overflow", "hidden");
32353         var ts = new Roo.bootstrap.panel.Tabs({
32354                 el: this.bodyEl.dom,
32355                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32356                 disableTooltips: this.config.disableTabTips,
32357                 toolbar : this.config.toolbar
32358             });
32359         
32360         if(this.config.hideTabs){
32361             ts.stripWrap.setDisplayed(false);
32362         }
32363         this.tabs = ts;
32364         ts.resizeTabs = this.config.resizeTabs === true;
32365         ts.minTabWidth = this.config.minTabWidth || 40;
32366         ts.maxTabWidth = this.config.maxTabWidth || 250;
32367         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32368         ts.monitorResize = false;
32369         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32370         ts.bodyEl.addClass('roo-layout-tabs-body');
32371         this.panels.each(this.initPanelAsTab, this);
32372     },
32373
32374     initPanelAsTab : function(panel){
32375         var ti = this.tabs.addTab(
32376                     panel.getEl().id,
32377                     panel.getTitle(), null,
32378                     this.config.closeOnTab && panel.isClosable()
32379             );
32380         if(panel.tabTip !== undefined){
32381             ti.setTooltip(panel.tabTip);
32382         }
32383         ti.on("activate", function(){
32384               this.setActivePanel(panel);
32385         }, this);
32386         
32387         if(this.config.closeOnTab){
32388             ti.on("beforeclose", function(t, e){
32389                 e.cancel = true;
32390                 this.remove(panel);
32391             }, this);
32392         }
32393         return ti;
32394     },
32395
32396     updatePanelTitle : function(panel, title)
32397     {
32398         if(this.activePanel == panel){
32399             this.updateTitle(title);
32400         }
32401         if(this.tabs){
32402             var ti = this.tabs.getTab(panel.getEl().id);
32403             ti.setText(title);
32404             if(panel.tabTip !== undefined){
32405                 ti.setTooltip(panel.tabTip);
32406             }
32407         }
32408     },
32409
32410     updateTitle : function(title){
32411         if(this.titleTextEl && !this.config.title){
32412             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32413         }
32414     },
32415
32416     setActivePanel : function(panel)
32417     {
32418         panel = this.getPanel(panel);
32419         if(this.activePanel && this.activePanel != panel){
32420             this.activePanel.setActiveState(false);
32421         }
32422         this.activePanel = panel;
32423         panel.setActiveState(true);
32424         if(this.panelSize){
32425             panel.setSize(this.panelSize.width, this.panelSize.height);
32426         }
32427         if(this.closeBtn){
32428             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32429         }
32430         this.updateTitle(panel.getTitle());
32431         if(this.tabs){
32432             this.fireEvent("invalidated", this);
32433         }
32434         this.fireEvent("panelactivated", this, panel);
32435     },
32436
32437     /**
32438      * Shows the specified panel.
32439      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32440      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32441      */
32442     showPanel : function(panel)
32443     {
32444         panel = this.getPanel(panel);
32445         if(panel){
32446             if(this.tabs){
32447                 var tab = this.tabs.getTab(panel.getEl().id);
32448                 if(tab.isHidden()){
32449                     this.tabs.unhideTab(tab.id);
32450                 }
32451                 tab.activate();
32452             }else{
32453                 this.setActivePanel(panel);
32454             }
32455         }
32456         return panel;
32457     },
32458
32459     /**
32460      * Get the active panel for this region.
32461      * @return {Roo.ContentPanel} The active panel or null
32462      */
32463     getActivePanel : function(){
32464         return this.activePanel;
32465     },
32466
32467     validateVisibility : function(){
32468         if(this.panels.getCount() < 1){
32469             this.updateTitle("&#160;");
32470             this.closeBtn.hide();
32471             this.hide();
32472         }else{
32473             if(!this.isVisible()){
32474                 this.show();
32475             }
32476         }
32477     },
32478
32479     /**
32480      * Adds the passed ContentPanel(s) to this region.
32481      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32482      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32483      */
32484     add : function(panel){
32485         if(arguments.length > 1){
32486             for(var i = 0, len = arguments.length; i < len; i++) {
32487                 this.add(arguments[i]);
32488             }
32489             return null;
32490         }
32491         if(this.hasPanel(panel)){
32492             this.showPanel(panel);
32493             return panel;
32494         }
32495         panel.setRegion(this);
32496         this.panels.add(panel);
32497         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32498             this.bodyEl.dom.appendChild(panel.getEl().dom);
32499             if(panel.background !== true){
32500                 this.setActivePanel(panel);
32501             }
32502             this.fireEvent("paneladded", this, panel);
32503             return panel;
32504         }
32505         if(!this.tabs){
32506             this.initTabs();
32507         }else{
32508             this.initPanelAsTab(panel);
32509         }
32510         
32511         
32512         if(panel.background !== true){
32513             this.tabs.activate(panel.getEl().id);
32514         }
32515         this.fireEvent("paneladded", this, panel);
32516         return panel;
32517     },
32518
32519     /**
32520      * Hides the tab for the specified panel.
32521      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32522      */
32523     hidePanel : function(panel){
32524         if(this.tabs && (panel = this.getPanel(panel))){
32525             this.tabs.hideTab(panel.getEl().id);
32526         }
32527     },
32528
32529     /**
32530      * Unhides the tab for a previously hidden panel.
32531      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32532      */
32533     unhidePanel : function(panel){
32534         if(this.tabs && (panel = this.getPanel(panel))){
32535             this.tabs.unhideTab(panel.getEl().id);
32536         }
32537     },
32538
32539     clearPanels : function(){
32540         while(this.panels.getCount() > 0){
32541              this.remove(this.panels.first());
32542         }
32543     },
32544
32545     /**
32546      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32547      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32548      * @param {Boolean} preservePanel Overrides the config preservePanel option
32549      * @return {Roo.ContentPanel} The panel that was removed
32550      */
32551     remove : function(panel, preservePanel)
32552     {
32553         panel = this.getPanel(panel);
32554         if(!panel){
32555             return null;
32556         }
32557         var e = {};
32558         this.fireEvent("beforeremove", this, panel, e);
32559         if(e.cancel === true){
32560             return null;
32561         }
32562         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32563         var panelId = panel.getId();
32564         this.panels.removeKey(panelId);
32565         if(preservePanel){
32566             document.body.appendChild(panel.getEl().dom);
32567         }
32568         if(this.tabs){
32569             this.tabs.removeTab(panel.getEl().id);
32570         }else if (!preservePanel){
32571             this.bodyEl.dom.removeChild(panel.getEl().dom);
32572         }
32573         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32574             var p = this.panels.first();
32575             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32576             tempEl.appendChild(p.getEl().dom);
32577             this.bodyEl.update("");
32578             this.bodyEl.dom.appendChild(p.getEl().dom);
32579             tempEl = null;
32580             this.updateTitle(p.getTitle());
32581             this.tabs = null;
32582             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32583             this.setActivePanel(p);
32584         }
32585         panel.setRegion(null);
32586         if(this.activePanel == panel){
32587             this.activePanel = null;
32588         }
32589         if(this.config.autoDestroy !== false && preservePanel !== true){
32590             try{panel.destroy();}catch(e){}
32591         }
32592         this.fireEvent("panelremoved", this, panel);
32593         return panel;
32594     },
32595
32596     /**
32597      * Returns the TabPanel component used by this region
32598      * @return {Roo.TabPanel}
32599      */
32600     getTabs : function(){
32601         return this.tabs;
32602     },
32603
32604     createTool : function(parentEl, className){
32605         var btn = Roo.DomHelper.append(parentEl, {
32606             tag: "div",
32607             cls: "x-layout-tools-button",
32608             children: [ {
32609                 tag: "div",
32610                 cls: "roo-layout-tools-button-inner " + className,
32611                 html: "&#160;"
32612             }]
32613         }, true);
32614         btn.addClassOnOver("roo-layout-tools-button-over");
32615         return btn;
32616     }
32617 });/*
32618  * Based on:
32619  * Ext JS Library 1.1.1
32620  * Copyright(c) 2006-2007, Ext JS, LLC.
32621  *
32622  * Originally Released Under LGPL - original licence link has changed is not relivant.
32623  *
32624  * Fork - LGPL
32625  * <script type="text/javascript">
32626  */
32627  
32628
32629
32630 /**
32631  * @class Roo.SplitLayoutRegion
32632  * @extends Roo.LayoutRegion
32633  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32634  */
32635 Roo.bootstrap.layout.Split = function(config){
32636     this.cursor = config.cursor;
32637     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32638 };
32639
32640 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32641 {
32642     splitTip : "Drag to resize.",
32643     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32644     useSplitTips : false,
32645
32646     applyConfig : function(config){
32647         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32648         
32649         if(config.split){
32650             if(!this.split){
32651                 
32652                 
32653                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,  {
32654                                 tag: "div",
32655                                 id: this.el.id + "-split",
32656                                 cls: "roo-layout-split roo-layout-split-"+this.position,
32657                                 html: "&#160;"
32658                 });
32659                 /** The SplitBar for this region 
32660                 * @type Roo.SplitBar */
32661                 // does not exist yet...
32662                 Roo.log([this.position, this.orientation]);
32663                 
32664                 this.split = new Roo.bootstrap.SplitBar({
32665                     dragElement : splitEl,
32666                     resizingElement: this.el,
32667                     orientation : this.orientation
32668                 });
32669                 
32670                 this.split.on("moved", this.onSplitMove, this);
32671                 this.split.useShim = config.useShim === true;
32672                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32673                 if(this.useSplitTips){
32674                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32675                 }
32676                 //if(config.collapsible){
32677                 //    this.split.el.on("dblclick", this.collapse,  this);
32678                 //}
32679             }
32680             if(typeof config.minSize != "undefined"){
32681                 this.split.minSize = config.minSize;
32682             }
32683             if(typeof config.maxSize != "undefined"){
32684                 this.split.maxSize = config.maxSize;
32685             }
32686             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32687                 this.hideSplitter();
32688             }
32689         }
32690     },
32691
32692     getHMaxSize : function(){
32693          var cmax = this.config.maxSize || 10000;
32694          var center = this.mgr.getRegion("center");
32695          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32696     },
32697
32698     getVMaxSize : function(){
32699          var cmax = this.config.maxSize || 10000;
32700          var center = this.mgr.getRegion("center");
32701          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32702     },
32703
32704     onSplitMove : function(split, newSize){
32705         this.fireEvent("resized", this, newSize);
32706     },
32707     
32708     /** 
32709      * Returns the {@link Roo.SplitBar} for this region.
32710      * @return {Roo.SplitBar}
32711      */
32712     getSplitBar : function(){
32713         return this.split;
32714     },
32715     
32716     hide : function(){
32717         this.hideSplitter();
32718         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32719     },
32720
32721     hideSplitter : function(){
32722         if(this.split){
32723             this.split.el.setLocation(-2000,-2000);
32724             this.split.el.hide();
32725         }
32726     },
32727
32728     show : function(){
32729         if(this.split){
32730             this.split.el.show();
32731         }
32732         Roo.bootstrap.layout.Split.superclass.show.call(this);
32733     },
32734     
32735     beforeSlide: function(){
32736         if(Roo.isGecko){// firefox overflow auto bug workaround
32737             this.bodyEl.clip();
32738             if(this.tabs) {
32739                 this.tabs.bodyEl.clip();
32740             }
32741             if(this.activePanel){
32742                 this.activePanel.getEl().clip();
32743                 
32744                 if(this.activePanel.beforeSlide){
32745                     this.activePanel.beforeSlide();
32746                 }
32747             }
32748         }
32749     },
32750     
32751     afterSlide : function(){
32752         if(Roo.isGecko){// firefox overflow auto bug workaround
32753             this.bodyEl.unclip();
32754             if(this.tabs) {
32755                 this.tabs.bodyEl.unclip();
32756             }
32757             if(this.activePanel){
32758                 this.activePanel.getEl().unclip();
32759                 if(this.activePanel.afterSlide){
32760                     this.activePanel.afterSlide();
32761                 }
32762             }
32763         }
32764     },
32765
32766     initAutoHide : function(){
32767         if(this.autoHide !== false){
32768             if(!this.autoHideHd){
32769                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32770                 this.autoHideHd = {
32771                     "mouseout": function(e){
32772                         if(!e.within(this.el, true)){
32773                             st.delay(500);
32774                         }
32775                     },
32776                     "mouseover" : function(e){
32777                         st.cancel();
32778                     },
32779                     scope : this
32780                 };
32781             }
32782             this.el.on(this.autoHideHd);
32783         }
32784     },
32785
32786     clearAutoHide : function(){
32787         if(this.autoHide !== false){
32788             this.el.un("mouseout", this.autoHideHd.mouseout);
32789             this.el.un("mouseover", this.autoHideHd.mouseover);
32790         }
32791     },
32792
32793     clearMonitor : function(){
32794         Roo.get(document).un("click", this.slideInIf, this);
32795     },
32796
32797     // these names are backwards but not changed for compat
32798     slideOut : function(){
32799         if(this.isSlid || this.el.hasActiveFx()){
32800             return;
32801         }
32802         this.isSlid = true;
32803         if(this.collapseBtn){
32804             this.collapseBtn.hide();
32805         }
32806         this.closeBtnState = this.closeBtn.getStyle('display');
32807         this.closeBtn.hide();
32808         if(this.stickBtn){
32809             this.stickBtn.show();
32810         }
32811         this.el.show();
32812         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32813         this.beforeSlide();
32814         this.el.setStyle("z-index", 10001);
32815         this.el.slideIn(this.getSlideAnchor(), {
32816             callback: function(){
32817                 this.afterSlide();
32818                 this.initAutoHide();
32819                 Roo.get(document).on("click", this.slideInIf, this);
32820                 this.fireEvent("slideshow", this);
32821             },
32822             scope: this,
32823             block: true
32824         });
32825     },
32826
32827     afterSlideIn : function(){
32828         this.clearAutoHide();
32829         this.isSlid = false;
32830         this.clearMonitor();
32831         this.el.setStyle("z-index", "");
32832         if(this.collapseBtn){
32833             this.collapseBtn.show();
32834         }
32835         this.closeBtn.setStyle('display', this.closeBtnState);
32836         if(this.stickBtn){
32837             this.stickBtn.hide();
32838         }
32839         this.fireEvent("slidehide", this);
32840     },
32841
32842     slideIn : function(cb){
32843         if(!this.isSlid || this.el.hasActiveFx()){
32844             Roo.callback(cb);
32845             return;
32846         }
32847         this.isSlid = false;
32848         this.beforeSlide();
32849         this.el.slideOut(this.getSlideAnchor(), {
32850             callback: function(){
32851                 this.el.setLeftTop(-10000, -10000);
32852                 this.afterSlide();
32853                 this.afterSlideIn();
32854                 Roo.callback(cb);
32855             },
32856             scope: this,
32857             block: true
32858         });
32859     },
32860     
32861     slideInIf : function(e){
32862         if(!e.within(this.el)){
32863             this.slideIn();
32864         }
32865     },
32866
32867     animateCollapse : function(){
32868         this.beforeSlide();
32869         this.el.setStyle("z-index", 20000);
32870         var anchor = this.getSlideAnchor();
32871         this.el.slideOut(anchor, {
32872             callback : function(){
32873                 this.el.setStyle("z-index", "");
32874                 this.collapsedEl.slideIn(anchor, {duration:.3});
32875                 this.afterSlide();
32876                 this.el.setLocation(-10000,-10000);
32877                 this.el.hide();
32878                 this.fireEvent("collapsed", this);
32879             },
32880             scope: this,
32881             block: true
32882         });
32883     },
32884
32885     animateExpand : function(){
32886         this.beforeSlide();
32887         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32888         this.el.setStyle("z-index", 20000);
32889         this.collapsedEl.hide({
32890             duration:.1
32891         });
32892         this.el.slideIn(this.getSlideAnchor(), {
32893             callback : function(){
32894                 this.el.setStyle("z-index", "");
32895                 this.afterSlide();
32896                 if(this.split){
32897                     this.split.el.show();
32898                 }
32899                 this.fireEvent("invalidated", this);
32900                 this.fireEvent("expanded", this);
32901             },
32902             scope: this,
32903             block: true
32904         });
32905     },
32906
32907     anchors : {
32908         "west" : "left",
32909         "east" : "right",
32910         "north" : "top",
32911         "south" : "bottom"
32912     },
32913
32914     sanchors : {
32915         "west" : "l",
32916         "east" : "r",
32917         "north" : "t",
32918         "south" : "b"
32919     },
32920
32921     canchors : {
32922         "west" : "tl-tr",
32923         "east" : "tr-tl",
32924         "north" : "tl-bl",
32925         "south" : "bl-tl"
32926     },
32927
32928     getAnchor : function(){
32929         return this.anchors[this.position];
32930     },
32931
32932     getCollapseAnchor : function(){
32933         return this.canchors[this.position];
32934     },
32935
32936     getSlideAnchor : function(){
32937         return this.sanchors[this.position];
32938     },
32939
32940     getAlignAdj : function(){
32941         var cm = this.cmargins;
32942         switch(this.position){
32943             case "west":
32944                 return [0, 0];
32945             break;
32946             case "east":
32947                 return [0, 0];
32948             break;
32949             case "north":
32950                 return [0, 0];
32951             break;
32952             case "south":
32953                 return [0, 0];
32954             break;
32955         }
32956     },
32957
32958     getExpandAdj : function(){
32959         var c = this.collapsedEl, cm = this.cmargins;
32960         switch(this.position){
32961             case "west":
32962                 return [-(cm.right+c.getWidth()+cm.left), 0];
32963             break;
32964             case "east":
32965                 return [cm.right+c.getWidth()+cm.left, 0];
32966             break;
32967             case "north":
32968                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32969             break;
32970             case "south":
32971                 return [0, cm.top+cm.bottom+c.getHeight()];
32972             break;
32973         }
32974     }
32975 });/*
32976  * Based on:
32977  * Ext JS Library 1.1.1
32978  * Copyright(c) 2006-2007, Ext JS, LLC.
32979  *
32980  * Originally Released Under LGPL - original licence link has changed is not relivant.
32981  *
32982  * Fork - LGPL
32983  * <script type="text/javascript">
32984  */
32985 /*
32986  * These classes are private internal classes
32987  */
32988 Roo.bootstrap.layout.Center = function(config){
32989     config.region = "center";
32990     Roo.bootstrap.layout.Region.call(this, config);
32991     this.visible = true;
32992     this.minWidth = config.minWidth || 20;
32993     this.minHeight = config.minHeight || 20;
32994 };
32995
32996 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
32997     hide : function(){
32998         // center panel can't be hidden
32999     },
33000     
33001     show : function(){
33002         // center panel can't be hidden
33003     },
33004     
33005     getMinWidth: function(){
33006         return this.minWidth;
33007     },
33008     
33009     getMinHeight: function(){
33010         return this.minHeight;
33011     }
33012 });
33013
33014
33015
33016
33017  
33018
33019
33020
33021
33022
33023 Roo.bootstrap.layout.North = function(config)
33024 {
33025     config.region = 'north';
33026     config.cursor = 'n-resize';
33027     
33028     Roo.bootstrap.layout.Split.call(this, config);
33029     if(this.split){
33030         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33031         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33032         this.split.el.addClass("roo-layout-split-v");
33033     }
33034     var size = config.initialSize || config.height;
33035     if(typeof size != "undefined"){
33036         this.el.setHeight(size);
33037     }
33038 };
33039 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33040 {
33041     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33042     getBox : function(){
33043         if(this.collapsed){
33044             return this.collapsedEl.getBox();
33045         }
33046         var box = this.el.getBox();
33047         if(this.split){
33048             box.height += this.split.el.getHeight();
33049         }
33050         return box;
33051     },
33052     
33053     updateBox : function(box){
33054         if(this.split && !this.collapsed){
33055             box.height -= this.split.el.getHeight();
33056             this.split.el.setLeft(box.x);
33057             this.split.el.setTop(box.y+box.height);
33058             this.split.el.setWidth(box.width);
33059         }
33060         if(this.collapsed){
33061             this.updateBody(box.width, null);
33062         }
33063         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33064     }
33065 });
33066
33067
33068
33069
33070
33071 Roo.bootstrap.layout.South = function(config){
33072     config.region = 'south';
33073     config.cursor = 's-resize';
33074     Roo.bootstrap.layout.Split.call(this, config);
33075     if(this.split){
33076         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33077         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33078         this.split.el.addClass("roo-layout-split-v");
33079     }
33080     var size = config.initialSize || config.height;
33081     if(typeof size != "undefined"){
33082         this.el.setHeight(size);
33083     }
33084 };
33085
33086 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33087     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33088     getBox : function(){
33089         if(this.collapsed){
33090             return this.collapsedEl.getBox();
33091         }
33092         var box = this.el.getBox();
33093         if(this.split){
33094             var sh = this.split.el.getHeight();
33095             box.height += sh;
33096             box.y -= sh;
33097         }
33098         return box;
33099     },
33100     
33101     updateBox : function(box){
33102         if(this.split && !this.collapsed){
33103             var sh = this.split.el.getHeight();
33104             box.height -= sh;
33105             box.y += sh;
33106             this.split.el.setLeft(box.x);
33107             this.split.el.setTop(box.y-sh);
33108             this.split.el.setWidth(box.width);
33109         }
33110         if(this.collapsed){
33111             this.updateBody(box.width, null);
33112         }
33113         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33114     }
33115 });
33116
33117 Roo.bootstrap.layout.East = function(config){
33118     config.region = "east";
33119     config.cursor = "e-resize";
33120     Roo.bootstrap.layout.Split.call(this, config);
33121     if(this.split){
33122         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33123         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33124         this.split.el.addClass("roo-layout-split-h");
33125     }
33126     var size = config.initialSize || config.width;
33127     if(typeof size != "undefined"){
33128         this.el.setWidth(size);
33129     }
33130 };
33131 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33132     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33133     getBox : function(){
33134         if(this.collapsed){
33135             return this.collapsedEl.getBox();
33136         }
33137         var box = this.el.getBox();
33138         if(this.split){
33139             var sw = this.split.el.getWidth();
33140             box.width += sw;
33141             box.x -= sw;
33142         }
33143         return box;
33144     },
33145
33146     updateBox : function(box){
33147         if(this.split && !this.collapsed){
33148             var sw = this.split.el.getWidth();
33149             box.width -= sw;
33150             this.split.el.setLeft(box.x);
33151             this.split.el.setTop(box.y);
33152             this.split.el.setHeight(box.height);
33153             box.x += sw;
33154         }
33155         if(this.collapsed){
33156             this.updateBody(null, box.height);
33157         }
33158         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33159     }
33160 });
33161
33162 Roo.bootstrap.layout.West = function(config){
33163     config.region = "west";
33164     config.cursor = "w-resize";
33165     
33166     Roo.bootstrap.layout.Split.call(this, config);
33167     if(this.split){
33168         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33169         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33170         this.split.el.addClass("roo-layout-split-h");
33171     }
33172     var size = config.initialSize || config.width;
33173     if(typeof size != "undefined"){
33174         this.el.setWidth(size);
33175     }
33176 };
33177 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33178     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33179     getBox : function(){
33180         if(this.collapsed){
33181             return this.collapsedEl.getBox();
33182         }
33183         var box = this.el.getBox();
33184         if(this.split){
33185             box.width += this.split.el.getWidth();
33186         }
33187         return box;
33188     },
33189     
33190     updateBox : function(box){
33191         if(this.split && !this.collapsed){
33192             var sw = this.split.el.getWidth();
33193             box.width -= sw;
33194             this.split.el.setLeft(box.x+box.width);
33195             this.split.el.setTop(box.y);
33196             this.split.el.setHeight(box.height);
33197         }
33198         if(this.collapsed){
33199             this.updateBody(null, box.height);
33200         }
33201         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33202     }
33203 });
33204 Roo.namespace("Roo.bootstrap.panel");/*
33205  * Based on:
33206  * Ext JS Library 1.1.1
33207  * Copyright(c) 2006-2007, Ext JS, LLC.
33208  *
33209  * Originally Released Under LGPL - original licence link has changed is not relivant.
33210  *
33211  * Fork - LGPL
33212  * <script type="text/javascript">
33213  */
33214 /**
33215  * @class Roo.ContentPanel
33216  * @extends Roo.util.Observable
33217  * A basic ContentPanel element.
33218  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33219  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33220  * @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
33221  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33222  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33223  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33224  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33225  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33226  * @cfg {String} title          The title for this panel
33227  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33228  * @cfg {String} url            Calls {@link #setUrl} with this value
33229  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33230  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33231  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33232  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33233
33234  * @constructor
33235  * Create a new ContentPanel.
33236  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33237  * @param {String/Object} config A string to set only the title or a config object
33238  * @param {String} content (optional) Set the HTML content for this panel
33239  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33240  */
33241 Roo.bootstrap.panel.Content = function( config){
33242     
33243     var el = config.el;
33244     var content = config.content;
33245
33246     if(config.autoCreate){ // xtype is available if this is called from factory
33247         el = Roo.id();
33248     }
33249     this.el = Roo.get(el);
33250     if(!this.el && config && config.autoCreate){
33251         if(typeof config.autoCreate == "object"){
33252             if(!config.autoCreate.id){
33253                 config.autoCreate.id = config.id||el;
33254             }
33255             this.el = Roo.DomHelper.append(document.body,
33256                         config.autoCreate, true);
33257         }else{
33258             var elcfg =  {   tag: "div",
33259                             cls: "roo-layout-inactive-content",
33260                             id: config.id||el
33261                             };
33262             if (config.html) {
33263                 elcfg.html = config.html;
33264                 
33265             }
33266                         
33267             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33268         }
33269     } 
33270     this.closable = false;
33271     this.loaded = false;
33272     this.active = false;
33273     if(typeof config == "string"){
33274         this.title = config;
33275     }else{
33276         Roo.apply(this, config);
33277     }
33278     /*
33279     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33280         this.wrapEl = this.el.wrap();
33281         this.toolbar.container = this.el.insertSibling(false, 'before');
33282         this.toolbar = new Roo.Toolbar(this.toolbar);
33283     }
33284     
33285     // xtype created footer. - not sure if will work as we normally have to render first..
33286     if (this.footer && !this.footer.el && this.footer.xtype) {
33287         if (!this.wrapEl) {
33288             this.wrapEl = this.el.wrap();
33289         }
33290     
33291         this.footer.container = this.wrapEl.createChild();
33292          
33293         this.footer = Roo.factory(this.footer, Roo);
33294         
33295     }
33296     */
33297     if(this.resizeEl){
33298         this.resizeEl = Roo.get(this.resizeEl, true);
33299     }else{
33300         this.resizeEl = this.el;
33301     }
33302     // handle view.xtype
33303     
33304  
33305     
33306     
33307     this.addEvents({
33308         /**
33309          * @event activate
33310          * Fires when this panel is activated. 
33311          * @param {Roo.ContentPanel} this
33312          */
33313         "activate" : true,
33314         /**
33315          * @event deactivate
33316          * Fires when this panel is activated. 
33317          * @param {Roo.ContentPanel} this
33318          */
33319         "deactivate" : true,
33320
33321         /**
33322          * @event resize
33323          * Fires when this panel is resized if fitToFrame is true.
33324          * @param {Roo.ContentPanel} this
33325          * @param {Number} width The width after any component adjustments
33326          * @param {Number} height The height after any component adjustments
33327          */
33328         "resize" : true,
33329         
33330          /**
33331          * @event render
33332          * Fires when this tab is created
33333          * @param {Roo.ContentPanel} this
33334          */
33335         "render" : true
33336         
33337         
33338         
33339     });
33340     
33341
33342     
33343     
33344     if(this.autoScroll){
33345         this.resizeEl.setStyle("overflow", "auto");
33346     } else {
33347         // fix randome scrolling
33348         this.el.on('scroll', function() {
33349             Roo.log('fix random scolling');
33350             this.scrollTo('top',0); 
33351         });
33352     }
33353     content = content || this.content;
33354     if(content){
33355         this.setContent(content);
33356     }
33357     if(config && config.url){
33358         this.setUrl(this.url, this.params, this.loadOnce);
33359     }
33360     
33361     
33362     
33363     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33364     
33365     if (this.view && typeof(this.view.xtype) != 'undefined') {
33366         this.view.el = this.el.appendChild(document.createElement("div"));
33367         this.view = Roo.factory(this.view); 
33368         this.view.render  &&  this.view.render(false, '');  
33369     }
33370     
33371     
33372     this.fireEvent('render', this);
33373 };
33374
33375 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33376     tabTip:'',
33377     setRegion : function(region){
33378         this.region = region;
33379         if(region){
33380            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33381         }else{
33382            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33383         } 
33384     },
33385     
33386     /**
33387      * Returns the toolbar for this Panel if one was configured. 
33388      * @return {Roo.Toolbar} 
33389      */
33390     getToolbar : function(){
33391         return this.toolbar;
33392     },
33393     
33394     setActiveState : function(active){
33395         this.active = active;
33396         if(!active){
33397             this.fireEvent("deactivate", this);
33398         }else{
33399             this.fireEvent("activate", this);
33400         }
33401     },
33402     /**
33403      * Updates this panel's element
33404      * @param {String} content The new content
33405      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33406     */
33407     setContent : function(content, loadScripts){
33408         this.el.update(content, loadScripts);
33409     },
33410
33411     ignoreResize : function(w, h){
33412         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33413             return true;
33414         }else{
33415             this.lastSize = {width: w, height: h};
33416             return false;
33417         }
33418     },
33419     /**
33420      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33421      * @return {Roo.UpdateManager} The UpdateManager
33422      */
33423     getUpdateManager : function(){
33424         return this.el.getUpdateManager();
33425     },
33426      /**
33427      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33428      * @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:
33429 <pre><code>
33430 panel.load({
33431     url: "your-url.php",
33432     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33433     callback: yourFunction,
33434     scope: yourObject, //(optional scope)
33435     discardUrl: false,
33436     nocache: false,
33437     text: "Loading...",
33438     timeout: 30,
33439     scripts: false
33440 });
33441 </code></pre>
33442      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33443      * 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.
33444      * @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}
33445      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33446      * @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.
33447      * @return {Roo.ContentPanel} this
33448      */
33449     load : function(){
33450         var um = this.el.getUpdateManager();
33451         um.update.apply(um, arguments);
33452         return this;
33453     },
33454
33455
33456     /**
33457      * 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.
33458      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33459      * @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)
33460      * @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)
33461      * @return {Roo.UpdateManager} The UpdateManager
33462      */
33463     setUrl : function(url, params, loadOnce){
33464         if(this.refreshDelegate){
33465             this.removeListener("activate", this.refreshDelegate);
33466         }
33467         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33468         this.on("activate", this.refreshDelegate);
33469         return this.el.getUpdateManager();
33470     },
33471     
33472     _handleRefresh : function(url, params, loadOnce){
33473         if(!loadOnce || !this.loaded){
33474             var updater = this.el.getUpdateManager();
33475             updater.update(url, params, this._setLoaded.createDelegate(this));
33476         }
33477     },
33478     
33479     _setLoaded : function(){
33480         this.loaded = true;
33481     }, 
33482     
33483     /**
33484      * Returns this panel's id
33485      * @return {String} 
33486      */
33487     getId : function(){
33488         return this.el.id;
33489     },
33490     
33491     /** 
33492      * Returns this panel's element - used by regiosn to add.
33493      * @return {Roo.Element} 
33494      */
33495     getEl : function(){
33496         return this.wrapEl || this.el;
33497     },
33498     
33499    
33500     
33501     adjustForComponents : function(width, height)
33502     {
33503         //Roo.log('adjustForComponents ');
33504         if(this.resizeEl != this.el){
33505             width -= this.el.getFrameWidth('lr');
33506             height -= this.el.getFrameWidth('tb');
33507         }
33508         if(this.toolbar){
33509             var te = this.toolbar.getEl();
33510             height -= te.getHeight();
33511             te.setWidth(width);
33512         }
33513         if(this.footer){
33514             var te = this.footer.getEl();
33515             Roo.log("footer:" + te.getHeight());
33516             
33517             height -= te.getHeight();
33518             te.setWidth(width);
33519         }
33520         
33521         
33522         if(this.adjustments){
33523             width += this.adjustments[0];
33524             height += this.adjustments[1];
33525         }
33526         return {"width": width, "height": height};
33527     },
33528     
33529     setSize : function(width, height){
33530         if(this.fitToFrame && !this.ignoreResize(width, height)){
33531             if(this.fitContainer && this.resizeEl != this.el){
33532                 this.el.setSize(width, height);
33533             }
33534             var size = this.adjustForComponents(width, height);
33535             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33536             this.fireEvent('resize', this, size.width, size.height);
33537         }
33538     },
33539     
33540     /**
33541      * Returns this panel's title
33542      * @return {String} 
33543      */
33544     getTitle : function(){
33545         return this.title;
33546     },
33547     
33548     /**
33549      * Set this panel's title
33550      * @param {String} title
33551      */
33552     setTitle : function(title){
33553         this.title = title;
33554         if(this.region){
33555             this.region.updatePanelTitle(this, title);
33556         }
33557     },
33558     
33559     /**
33560      * Returns true is this panel was configured to be closable
33561      * @return {Boolean} 
33562      */
33563     isClosable : function(){
33564         return this.closable;
33565     },
33566     
33567     beforeSlide : function(){
33568         this.el.clip();
33569         this.resizeEl.clip();
33570     },
33571     
33572     afterSlide : function(){
33573         this.el.unclip();
33574         this.resizeEl.unclip();
33575     },
33576     
33577     /**
33578      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33579      *   Will fail silently if the {@link #setUrl} method has not been called.
33580      *   This does not activate the panel, just updates its content.
33581      */
33582     refresh : function(){
33583         if(this.refreshDelegate){
33584            this.loaded = false;
33585            this.refreshDelegate();
33586         }
33587     },
33588     
33589     /**
33590      * Destroys this panel
33591      */
33592     destroy : function(){
33593         this.el.removeAllListeners();
33594         var tempEl = document.createElement("span");
33595         tempEl.appendChild(this.el.dom);
33596         tempEl.innerHTML = "";
33597         this.el.remove();
33598         this.el = null;
33599     },
33600     
33601     /**
33602      * form - if the content panel contains a form - this is a reference to it.
33603      * @type {Roo.form.Form}
33604      */
33605     form : false,
33606     /**
33607      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33608      *    This contains a reference to it.
33609      * @type {Roo.View}
33610      */
33611     view : false,
33612     
33613       /**
33614      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33615      * <pre><code>
33616
33617 layout.addxtype({
33618        xtype : 'Form',
33619        items: [ .... ]
33620    }
33621 );
33622
33623 </code></pre>
33624      * @param {Object} cfg Xtype definition of item to add.
33625      */
33626     
33627     
33628     getChildContainer: function () {
33629         return this.getEl();
33630     }
33631     
33632     
33633     /*
33634         var  ret = new Roo.factory(cfg);
33635         return ret;
33636         
33637         
33638         // add form..
33639         if (cfg.xtype.match(/^Form$/)) {
33640             
33641             var el;
33642             //if (this.footer) {
33643             //    el = this.footer.container.insertSibling(false, 'before');
33644             //} else {
33645                 el = this.el.createChild();
33646             //}
33647
33648             this.form = new  Roo.form.Form(cfg);
33649             
33650             
33651             if ( this.form.allItems.length) {
33652                 this.form.render(el.dom);
33653             }
33654             return this.form;
33655         }
33656         // should only have one of theses..
33657         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33658             // views.. should not be just added - used named prop 'view''
33659             
33660             cfg.el = this.el.appendChild(document.createElement("div"));
33661             // factory?
33662             
33663             var ret = new Roo.factory(cfg);
33664              
33665              ret.render && ret.render(false, ''); // render blank..
33666             this.view = ret;
33667             return ret;
33668         }
33669         return false;
33670     }
33671     \*/
33672 });
33673  
33674 /**
33675  * @class Roo.bootstrap.panel.Grid
33676  * @extends Roo.bootstrap.panel.Content
33677  * @constructor
33678  * Create a new GridPanel.
33679  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33680  * @param {String/Object} config A string to set only the panel's title, or a config object
33681
33682   new Roo.bootstrap.panel.Grid({
33683                 grid: .....
33684                 ....
33685   }
33686
33687  */
33688
33689
33690
33691 Roo.bootstrap.panel.Grid = function(config){
33692     
33693   
33694     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33695         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33696         
33697     this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33698     config.el = this.wrapper;
33699     
33700     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33701     
33702     if(this.toolbar){
33703         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33704     }
33705     // xtype created footer. - not sure if will work as we normally have to render first..
33706     if (this.footer && !this.footer.el && this.footer.xtype) {
33707         
33708         this.footer.container = this.grid.getView().getFooterPanel(true);
33709         this.footer.dataSource = this.grid.dataSource;
33710         this.footer = Roo.factory(this.footer, Roo);
33711         
33712     }
33713     
33714     
33715     config.grid.monitorWindowResize = false; // turn off autosizing
33716     config.grid.autoHeight = false;
33717     config.grid.autoWidth = false;
33718     this.grid = config.grid;
33719     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33720     
33721      
33722 };
33723
33724 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33725     getId : function(){
33726         return this.grid.id;
33727     },
33728     
33729     /**
33730      * Returns the grid for this panel
33731      * @return {Roo.bootstrap.Table} 
33732      */
33733     getGrid : function(){
33734         return this.grid;    
33735     },
33736     
33737     setSize : function(width, height){
33738         if(!this.ignoreResize(width, height)){
33739             var grid = this.grid;
33740             var size = this.adjustForComponents(width, height);
33741             grid.getGridEl().setSize(size.width, size.height);
33742             var thd = grid.getGridEl().select('thead');
33743             var tbd = grid.getGridEl().select('tbody');
33744             grid.autoSize();
33745         }
33746     },
33747      
33748     
33749     
33750     beforeSlide : function(){
33751         this.grid.getView().scroller.clip();
33752     },
33753     
33754     afterSlide : function(){
33755         this.grid.getView().scroller.unclip();
33756     },
33757     
33758     destroy : function(){
33759         this.grid.destroy();
33760         delete this.grid;
33761         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
33762     }
33763 });
33764
33765 /**
33766  * @class Roo.bootstrap.panel.Nest
33767  * @extends Roo.bootstrap.panel.Content
33768  * @constructor
33769  * Create a new Panel, that can contain a layout.Border.
33770  * 
33771  * 
33772  * @param {Roo.BorderLayout} layout The layout for this panel
33773  * @param {String/Object} config A string to set only the title or a config object
33774  */
33775 Roo.bootstrap.panel.Nest = function(config)
33776 {
33777     // construct with only one argument..
33778     /* FIXME - implement nicer consturctors
33779     if (layout.layout) {
33780         config = layout;
33781         layout = config.layout;
33782         delete config.layout;
33783     }
33784     if (layout.xtype && !layout.getEl) {
33785         // then layout needs constructing..
33786         layout = Roo.factory(layout, Roo);
33787     }
33788     */
33789     
33790     config.el =  config.layout.getEl();
33791     
33792     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
33793     
33794     config.layout.monitorWindowResize = false; // turn off autosizing
33795     this.layout = config.layout;
33796     this.layout.getEl().addClass("roo-layout-nested-layout");
33797     
33798     
33799     
33800     
33801 };
33802
33803 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
33804
33805     setSize : function(width, height){
33806         if(!this.ignoreResize(width, height)){
33807             var size = this.adjustForComponents(width, height);
33808             var el = this.layout.getEl();
33809             el.setSize(size.width, size.height);
33810             var touch = el.dom.offsetWidth;
33811             this.layout.layout();
33812             // ie requires a double layout on the first pass
33813             if(Roo.isIE && !this.initialized){
33814                 this.initialized = true;
33815                 this.layout.layout();
33816             }
33817         }
33818     },
33819     
33820     // activate all subpanels if not currently active..
33821     
33822     setActiveState : function(active){
33823         this.active = active;
33824         if(!active){
33825             this.fireEvent("deactivate", this);
33826             return;
33827         }
33828         
33829         this.fireEvent("activate", this);
33830         // not sure if this should happen before or after..
33831         if (!this.layout) {
33832             return; // should not happen..
33833         }
33834         var reg = false;
33835         for (var r in this.layout.regions) {
33836             reg = this.layout.getRegion(r);
33837             if (reg.getActivePanel()) {
33838                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33839                 reg.setActivePanel(reg.getActivePanel());
33840                 continue;
33841             }
33842             if (!reg.panels.length) {
33843                 continue;
33844             }
33845             reg.showPanel(reg.getPanel(0));
33846         }
33847         
33848         
33849         
33850         
33851     },
33852     
33853     /**
33854      * Returns the nested BorderLayout for this panel
33855      * @return {Roo.BorderLayout} 
33856      */
33857     getLayout : function(){
33858         return this.layout;
33859     },
33860     
33861      /**
33862      * Adds a xtype elements to the layout of the nested panel
33863      * <pre><code>
33864
33865 panel.addxtype({
33866        xtype : 'ContentPanel',
33867        region: 'west',
33868        items: [ .... ]
33869    }
33870 );
33871
33872 panel.addxtype({
33873         xtype : 'NestedLayoutPanel',
33874         region: 'west',
33875         layout: {
33876            center: { },
33877            west: { }   
33878         },
33879         items : [ ... list of content panels or nested layout panels.. ]
33880    }
33881 );
33882 </code></pre>
33883      * @param {Object} cfg Xtype definition of item to add.
33884      */
33885     addxtype : function(cfg) {
33886         return this.layout.addxtype(cfg);
33887     
33888     }
33889 });        /*
33890  * Based on:
33891  * Ext JS Library 1.1.1
33892  * Copyright(c) 2006-2007, Ext JS, LLC.
33893  *
33894  * Originally Released Under LGPL - original licence link has changed is not relivant.
33895  *
33896  * Fork - LGPL
33897  * <script type="text/javascript">
33898  */
33899 /**
33900  * @class Roo.TabPanel
33901  * @extends Roo.util.Observable
33902  * A lightweight tab container.
33903  * <br><br>
33904  * Usage:
33905  * <pre><code>
33906 // basic tabs 1, built from existing content
33907 var tabs = new Roo.TabPanel("tabs1");
33908 tabs.addTab("script", "View Script");
33909 tabs.addTab("markup", "View Markup");
33910 tabs.activate("script");
33911
33912 // more advanced tabs, built from javascript
33913 var jtabs = new Roo.TabPanel("jtabs");
33914 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
33915
33916 // set up the UpdateManager
33917 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
33918 var updater = tab2.getUpdateManager();
33919 updater.setDefaultUrl("ajax1.htm");
33920 tab2.on('activate', updater.refresh, updater, true);
33921
33922 // Use setUrl for Ajax loading
33923 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
33924 tab3.setUrl("ajax2.htm", null, true);
33925
33926 // Disabled tab
33927 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
33928 tab4.disable();
33929
33930 jtabs.activate("jtabs-1");
33931  * </code></pre>
33932  * @constructor
33933  * Create a new TabPanel.
33934  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
33935  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
33936  */
33937 Roo.bootstrap.panel.Tabs = function(config){
33938     /**
33939     * The container element for this TabPanel.
33940     * @type Roo.Element
33941     */
33942     this.el = Roo.get(config.el);
33943     delete config.el;
33944     if(config){
33945         if(typeof config == "boolean"){
33946             this.tabPosition = config ? "bottom" : "top";
33947         }else{
33948             Roo.apply(this, config);
33949         }
33950     }
33951     
33952     if(this.tabPosition == "bottom"){
33953         this.bodyEl = Roo.get(this.createBody(this.el.dom));
33954         this.el.addClass("roo-tabs-bottom");
33955     }
33956     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
33957     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
33958     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
33959     if(Roo.isIE){
33960         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
33961     }
33962     if(this.tabPosition != "bottom"){
33963         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
33964          * @type Roo.Element
33965          */
33966         this.bodyEl = Roo.get(this.createBody(this.el.dom));
33967         this.el.addClass("roo-tabs-top");
33968     }
33969     this.items = [];
33970
33971     this.bodyEl.setStyle("position", "relative");
33972
33973     this.active = null;
33974     this.activateDelegate = this.activate.createDelegate(this);
33975
33976     this.addEvents({
33977         /**
33978          * @event tabchange
33979          * Fires when the active tab changes
33980          * @param {Roo.TabPanel} this
33981          * @param {Roo.TabPanelItem} activePanel The new active tab
33982          */
33983         "tabchange": true,
33984         /**
33985          * @event beforetabchange
33986          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
33987          * @param {Roo.TabPanel} this
33988          * @param {Object} e Set cancel to true on this object to cancel the tab change
33989          * @param {Roo.TabPanelItem} tab The tab being changed to
33990          */
33991         "beforetabchange" : true
33992     });
33993
33994     Roo.EventManager.onWindowResize(this.onResize, this);
33995     this.cpad = this.el.getPadding("lr");
33996     this.hiddenCount = 0;
33997
33998
33999     // toolbar on the tabbar support...
34000     if (this.toolbar) {
34001         alert("no toolbar support yet");
34002         this.toolbar  = false;
34003         /*
34004         var tcfg = this.toolbar;
34005         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34006         this.toolbar = new Roo.Toolbar(tcfg);
34007         if (Roo.isSafari) {
34008             var tbl = tcfg.container.child('table', true);
34009             tbl.setAttribute('width', '100%');
34010         }
34011         */
34012         
34013     }
34014    
34015
34016
34017     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34018 };
34019
34020 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34021     /*
34022      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34023      */
34024     tabPosition : "top",
34025     /*
34026      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34027      */
34028     currentTabWidth : 0,
34029     /*
34030      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34031      */
34032     minTabWidth : 40,
34033     /*
34034      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34035      */
34036     maxTabWidth : 250,
34037     /*
34038      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34039      */
34040     preferredTabWidth : 175,
34041     /*
34042      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34043      */
34044     resizeTabs : false,
34045     /*
34046      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34047      */
34048     monitorResize : true,
34049     /*
34050      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34051      */
34052     toolbar : false,
34053
34054     /**
34055      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34056      * @param {String} id The id of the div to use <b>or create</b>
34057      * @param {String} text The text for the tab
34058      * @param {String} content (optional) Content to put in the TabPanelItem body
34059      * @param {Boolean} closable (optional) True to create a close icon on the tab
34060      * @return {Roo.TabPanelItem} The created TabPanelItem
34061      */
34062     addTab : function(id, text, content, closable)
34063     {
34064         var item = new Roo.bootstrap.panel.TabItem({
34065             panel: this,
34066             id : id,
34067             text : text,
34068             closable : closable
34069         });
34070         this.addTabItem(item);
34071         if(content){
34072             item.setContent(content);
34073         }
34074         return item;
34075     },
34076
34077     /**
34078      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34079      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34080      * @return {Roo.TabPanelItem}
34081      */
34082     getTab : function(id){
34083         return this.items[id];
34084     },
34085
34086     /**
34087      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34088      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34089      */
34090     hideTab : function(id){
34091         var t = this.items[id];
34092         if(!t.isHidden()){
34093            t.setHidden(true);
34094            this.hiddenCount++;
34095            this.autoSizeTabs();
34096         }
34097     },
34098
34099     /**
34100      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34101      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34102      */
34103     unhideTab : function(id){
34104         var t = this.items[id];
34105         if(t.isHidden()){
34106            t.setHidden(false);
34107            this.hiddenCount--;
34108            this.autoSizeTabs();
34109         }
34110     },
34111
34112     /**
34113      * Adds an existing {@link Roo.TabPanelItem}.
34114      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34115      */
34116     addTabItem : function(item){
34117         this.items[item.id] = item;
34118         this.items.push(item);
34119       //  if(this.resizeTabs){
34120     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34121   //         this.autoSizeTabs();
34122 //        }else{
34123 //            item.autoSize();
34124        // }
34125     },
34126
34127     /**
34128      * Removes a {@link Roo.TabPanelItem}.
34129      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34130      */
34131     removeTab : function(id){
34132         var items = this.items;
34133         var tab = items[id];
34134         if(!tab) { return; }
34135         var index = items.indexOf(tab);
34136         if(this.active == tab && items.length > 1){
34137             var newTab = this.getNextAvailable(index);
34138             if(newTab) {
34139                 newTab.activate();
34140             }
34141         }
34142         this.stripEl.dom.removeChild(tab.pnode.dom);
34143         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34144             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34145         }
34146         items.splice(index, 1);
34147         delete this.items[tab.id];
34148         tab.fireEvent("close", tab);
34149         tab.purgeListeners();
34150         this.autoSizeTabs();
34151     },
34152
34153     getNextAvailable : function(start){
34154         var items = this.items;
34155         var index = start;
34156         // look for a next tab that will slide over to
34157         // replace the one being removed
34158         while(index < items.length){
34159             var item = items[++index];
34160             if(item && !item.isHidden()){
34161                 return item;
34162             }
34163         }
34164         // if one isn't found select the previous tab (on the left)
34165         index = start;
34166         while(index >= 0){
34167             var item = items[--index];
34168             if(item && !item.isHidden()){
34169                 return item;
34170             }
34171         }
34172         return null;
34173     },
34174
34175     /**
34176      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34177      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34178      */
34179     disableTab : function(id){
34180         var tab = this.items[id];
34181         if(tab && this.active != tab){
34182             tab.disable();
34183         }
34184     },
34185
34186     /**
34187      * Enables a {@link Roo.TabPanelItem} that is disabled.
34188      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34189      */
34190     enableTab : function(id){
34191         var tab = this.items[id];
34192         tab.enable();
34193     },
34194
34195     /**
34196      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34197      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34198      * @return {Roo.TabPanelItem} The TabPanelItem.
34199      */
34200     activate : function(id){
34201         var tab = this.items[id];
34202         if(!tab){
34203             return null;
34204         }
34205         if(tab == this.active || tab.disabled){
34206             return tab;
34207         }
34208         var e = {};
34209         this.fireEvent("beforetabchange", this, e, tab);
34210         if(e.cancel !== true && !tab.disabled){
34211             if(this.active){
34212                 this.active.hide();
34213             }
34214             this.active = this.items[id];
34215             this.active.show();
34216             this.fireEvent("tabchange", this, this.active);
34217         }
34218         return tab;
34219     },
34220
34221     /**
34222      * Gets the active {@link Roo.TabPanelItem}.
34223      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34224      */
34225     getActiveTab : function(){
34226         return this.active;
34227     },
34228
34229     /**
34230      * Updates the tab body element to fit the height of the container element
34231      * for overflow scrolling
34232      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34233      */
34234     syncHeight : function(targetHeight){
34235         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34236         var bm = this.bodyEl.getMargins();
34237         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34238         this.bodyEl.setHeight(newHeight);
34239         return newHeight;
34240     },
34241
34242     onResize : function(){
34243         if(this.monitorResize){
34244             this.autoSizeTabs();
34245         }
34246     },
34247
34248     /**
34249      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34250      */
34251     beginUpdate : function(){
34252         this.updating = true;
34253     },
34254
34255     /**
34256      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34257      */
34258     endUpdate : function(){
34259         this.updating = false;
34260         this.autoSizeTabs();
34261     },
34262
34263     /**
34264      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34265      */
34266     autoSizeTabs : function(){
34267         var count = this.items.length;
34268         var vcount = count - this.hiddenCount;
34269         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34270             return;
34271         }
34272         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34273         var availWidth = Math.floor(w / vcount);
34274         var b = this.stripBody;
34275         if(b.getWidth() > w){
34276             var tabs = this.items;
34277             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34278             if(availWidth < this.minTabWidth){
34279                 /*if(!this.sleft){    // incomplete scrolling code
34280                     this.createScrollButtons();
34281                 }
34282                 this.showScroll();
34283                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34284             }
34285         }else{
34286             if(this.currentTabWidth < this.preferredTabWidth){
34287                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34288             }
34289         }
34290     },
34291
34292     /**
34293      * Returns the number of tabs in this TabPanel.
34294      * @return {Number}
34295      */
34296      getCount : function(){
34297          return this.items.length;
34298      },
34299
34300     /**
34301      * Resizes all the tabs to the passed width
34302      * @param {Number} The new width
34303      */
34304     setTabWidth : function(width){
34305         this.currentTabWidth = width;
34306         for(var i = 0, len = this.items.length; i < len; i++) {
34307                 if(!this.items[i].isHidden()) {
34308                 this.items[i].setWidth(width);
34309             }
34310         }
34311     },
34312
34313     /**
34314      * Destroys this TabPanel
34315      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34316      */
34317     destroy : function(removeEl){
34318         Roo.EventManager.removeResizeListener(this.onResize, this);
34319         for(var i = 0, len = this.items.length; i < len; i++){
34320             this.items[i].purgeListeners();
34321         }
34322         if(removeEl === true){
34323             this.el.update("");
34324             this.el.remove();
34325         }
34326     },
34327     
34328     createStrip : function(container)
34329     {
34330         var strip = document.createElement("nav");
34331         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34332         container.appendChild(strip);
34333         return strip;
34334     },
34335     
34336     createStripList : function(strip)
34337     {
34338         // div wrapper for retard IE
34339         // returns the "tr" element.
34340         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34341         //'<div class="x-tabs-strip-wrap">'+
34342           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34343           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34344         return strip.firstChild; //.firstChild.firstChild.firstChild;
34345     },
34346     createBody : function(container)
34347     {
34348         var body = document.createElement("div");
34349         Roo.id(body, "tab-body");
34350         //Roo.fly(body).addClass("x-tabs-body");
34351         Roo.fly(body).addClass("tab-content");
34352         container.appendChild(body);
34353         return body;
34354     },
34355     createItemBody :function(bodyEl, id){
34356         var body = Roo.getDom(id);
34357         if(!body){
34358             body = document.createElement("div");
34359             body.id = id;
34360         }
34361         //Roo.fly(body).addClass("x-tabs-item-body");
34362         Roo.fly(body).addClass("tab-pane");
34363          bodyEl.insertBefore(body, bodyEl.firstChild);
34364         return body;
34365     },
34366     /** @private */
34367     createStripElements :  function(stripEl, text, closable)
34368     {
34369         var td = document.createElement("li"); // was td..
34370         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34371         //stripEl.appendChild(td);
34372         /*if(closable){
34373             td.className = "x-tabs-closable";
34374             if(!this.closeTpl){
34375                 this.closeTpl = new Roo.Template(
34376                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34377                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34378                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34379                 );
34380             }
34381             var el = this.closeTpl.overwrite(td, {"text": text});
34382             var close = el.getElementsByTagName("div")[0];
34383             var inner = el.getElementsByTagName("em")[0];
34384             return {"el": el, "close": close, "inner": inner};
34385         } else {
34386         */
34387         // not sure what this is..
34388             if(!this.tabTpl){
34389                 //this.tabTpl = new Roo.Template(
34390                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34391                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34392                 //);
34393                 this.tabTpl = new Roo.Template(
34394                    '<a href="#">' +
34395                    '<span unselectable="on"' +
34396                             (this.disableTooltips ? '' : ' title="{text}"') +
34397                             ' >{text}</span></span></a>'
34398                 );
34399                 
34400             }
34401             var el = this.tabTpl.overwrite(td, {"text": text});
34402             var inner = el.getElementsByTagName("span")[0];
34403             return {"el": el, "inner": inner};
34404         //}
34405     }
34406         
34407     
34408 });
34409
34410 /**
34411  * @class Roo.TabPanelItem
34412  * @extends Roo.util.Observable
34413  * Represents an individual item (tab plus body) in a TabPanel.
34414  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34415  * @param {String} id The id of this TabPanelItem
34416  * @param {String} text The text for the tab of this TabPanelItem
34417  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34418  */
34419 Roo.bootstrap.panel.TabItem = function(config){
34420     /**
34421      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34422      * @type Roo.TabPanel
34423      */
34424     this.tabPanel = config.panel;
34425     /**
34426      * The id for this TabPanelItem
34427      * @type String
34428      */
34429     this.id = config.id;
34430     /** @private */
34431     this.disabled = false;
34432     /** @private */
34433     this.text = config.text;
34434     /** @private */
34435     this.loaded = false;
34436     this.closable = config.closable;
34437
34438     /**
34439      * The body element for this TabPanelItem.
34440      * @type Roo.Element
34441      */
34442     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34443     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34444     this.bodyEl.setStyle("display", "block");
34445     this.bodyEl.setStyle("zoom", "1");
34446     //this.hideAction();
34447
34448     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34449     /** @private */
34450     this.el = Roo.get(els.el);
34451     this.inner = Roo.get(els.inner, true);
34452     this.textEl = Roo.get(this.el.dom.firstChild, true);
34453     this.pnode = Roo.get(els.el.parentNode, true);
34454     this.el.on("mousedown", this.onTabMouseDown, this);
34455     this.el.on("click", this.onTabClick, this);
34456     /** @private */
34457     if(config.closable){
34458         var c = Roo.get(els.close, true);
34459         c.dom.title = this.closeText;
34460         c.addClassOnOver("close-over");
34461         c.on("click", this.closeClick, this);
34462      }
34463
34464     this.addEvents({
34465          /**
34466          * @event activate
34467          * Fires when this tab becomes the active tab.
34468          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34469          * @param {Roo.TabPanelItem} this
34470          */
34471         "activate": true,
34472         /**
34473          * @event beforeclose
34474          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34475          * @param {Roo.TabPanelItem} this
34476          * @param {Object} e Set cancel to true on this object to cancel the close.
34477          */
34478         "beforeclose": true,
34479         /**
34480          * @event close
34481          * Fires when this tab is closed.
34482          * @param {Roo.TabPanelItem} this
34483          */
34484          "close": true,
34485         /**
34486          * @event deactivate
34487          * Fires when this tab is no longer the active tab.
34488          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34489          * @param {Roo.TabPanelItem} this
34490          */
34491          "deactivate" : true
34492     });
34493     this.hidden = false;
34494
34495     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34496 };
34497
34498 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34499            {
34500     purgeListeners : function(){
34501        Roo.util.Observable.prototype.purgeListeners.call(this);
34502        this.el.removeAllListeners();
34503     },
34504     /**
34505      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34506      */
34507     show : function(){
34508         this.pnode.addClass("active");
34509         this.showAction();
34510         if(Roo.isOpera){
34511             this.tabPanel.stripWrap.repaint();
34512         }
34513         this.fireEvent("activate", this.tabPanel, this);
34514     },
34515
34516     /**
34517      * Returns true if this tab is the active tab.
34518      * @return {Boolean}
34519      */
34520     isActive : function(){
34521         return this.tabPanel.getActiveTab() == this;
34522     },
34523
34524     /**
34525      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34526      */
34527     hide : function(){
34528         this.pnode.removeClass("active");
34529         this.hideAction();
34530         this.fireEvent("deactivate", this.tabPanel, this);
34531     },
34532
34533     hideAction : function(){
34534         this.bodyEl.hide();
34535         this.bodyEl.setStyle("position", "absolute");
34536         this.bodyEl.setLeft("-20000px");
34537         this.bodyEl.setTop("-20000px");
34538     },
34539
34540     showAction : function(){
34541         this.bodyEl.setStyle("position", "relative");
34542         this.bodyEl.setTop("");
34543         this.bodyEl.setLeft("");
34544         this.bodyEl.show();
34545     },
34546
34547     /**
34548      * Set the tooltip for the tab.
34549      * @param {String} tooltip The tab's tooltip
34550      */
34551     setTooltip : function(text){
34552         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34553             this.textEl.dom.qtip = text;
34554             this.textEl.dom.removeAttribute('title');
34555         }else{
34556             this.textEl.dom.title = text;
34557         }
34558     },
34559
34560     onTabClick : function(e){
34561         e.preventDefault();
34562         this.tabPanel.activate(this.id);
34563     },
34564
34565     onTabMouseDown : function(e){
34566         e.preventDefault();
34567         this.tabPanel.activate(this.id);
34568     },
34569 /*
34570     getWidth : function(){
34571         return this.inner.getWidth();
34572     },
34573
34574     setWidth : function(width){
34575         var iwidth = width - this.pnode.getPadding("lr");
34576         this.inner.setWidth(iwidth);
34577         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34578         this.pnode.setWidth(width);
34579     },
34580 */
34581     /**
34582      * Show or hide the tab
34583      * @param {Boolean} hidden True to hide or false to show.
34584      */
34585     setHidden : function(hidden){
34586         this.hidden = hidden;
34587         this.pnode.setStyle("display", hidden ? "none" : "");
34588     },
34589
34590     /**
34591      * Returns true if this tab is "hidden"
34592      * @return {Boolean}
34593      */
34594     isHidden : function(){
34595         return this.hidden;
34596     },
34597
34598     /**
34599      * Returns the text for this tab
34600      * @return {String}
34601      */
34602     getText : function(){
34603         return this.text;
34604     },
34605     /*
34606     autoSize : function(){
34607         //this.el.beginMeasure();
34608         this.textEl.setWidth(1);
34609         /*
34610          *  #2804 [new] Tabs in Roojs
34611          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34612          */
34613         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34614         //this.el.endMeasure();
34615     //},
34616
34617     /**
34618      * Sets the text for the tab (Note: this also sets the tooltip text)
34619      * @param {String} text The tab's text and tooltip
34620      */
34621     setText : function(text){
34622         this.text = text;
34623         this.textEl.update(text);
34624         this.setTooltip(text);
34625         //if(!this.tabPanel.resizeTabs){
34626         //    this.autoSize();
34627         //}
34628     },
34629     /**
34630      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34631      */
34632     activate : function(){
34633         this.tabPanel.activate(this.id);
34634     },
34635
34636     /**
34637      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34638      */
34639     disable : function(){
34640         if(this.tabPanel.active != this){
34641             this.disabled = true;
34642             this.pnode.addClass("disabled");
34643         }
34644     },
34645
34646     /**
34647      * Enables this TabPanelItem if it was previously disabled.
34648      */
34649     enable : function(){
34650         this.disabled = false;
34651         this.pnode.removeClass("disabled");
34652     },
34653
34654     /**
34655      * Sets the content for this TabPanelItem.
34656      * @param {String} content The content
34657      * @param {Boolean} loadScripts true to look for and load scripts
34658      */
34659     setContent : function(content, loadScripts){
34660         this.bodyEl.update(content, loadScripts);
34661     },
34662
34663     /**
34664      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34665      * @return {Roo.UpdateManager} The UpdateManager
34666      */
34667     getUpdateManager : function(){
34668         return this.bodyEl.getUpdateManager();
34669     },
34670
34671     /**
34672      * Set a URL to be used to load the content for this TabPanelItem.
34673      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34674      * @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)
34675      * @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)
34676      * @return {Roo.UpdateManager} The UpdateManager
34677      */
34678     setUrl : function(url, params, loadOnce){
34679         if(this.refreshDelegate){
34680             this.un('activate', this.refreshDelegate);
34681         }
34682         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34683         this.on("activate", this.refreshDelegate);
34684         return this.bodyEl.getUpdateManager();
34685     },
34686
34687     /** @private */
34688     _handleRefresh : function(url, params, loadOnce){
34689         if(!loadOnce || !this.loaded){
34690             var updater = this.bodyEl.getUpdateManager();
34691             updater.update(url, params, this._setLoaded.createDelegate(this));
34692         }
34693     },
34694
34695     /**
34696      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
34697      *   Will fail silently if the setUrl method has not been called.
34698      *   This does not activate the panel, just updates its content.
34699      */
34700     refresh : function(){
34701         if(this.refreshDelegate){
34702            this.loaded = false;
34703            this.refreshDelegate();
34704         }
34705     },
34706
34707     /** @private */
34708     _setLoaded : function(){
34709         this.loaded = true;
34710     },
34711
34712     /** @private */
34713     closeClick : function(e){
34714         var o = {};
34715         e.stopEvent();
34716         this.fireEvent("beforeclose", this, o);
34717         if(o.cancel !== true){
34718             this.tabPanel.removeTab(this.id);
34719         }
34720     },
34721     /**
34722      * The text displayed in the tooltip for the close icon.
34723      * @type String
34724      */
34725     closeText : "Close this tab"
34726 });