c631f78448fb68a1bbd24b4af3264673f402f948
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     },
1517     /**
1518      * Sets the url of the image - used to update it
1519      * @param {String} url the url of the image
1520      */
1521     
1522     setSrc : function(url)
1523     {
1524         this.src =  url;
1525         this.el.select('img', true).first().dom.src =  url;
1526     }
1527     
1528     
1529    
1530 });
1531
1532  /*
1533  * - LGPL
1534  *
1535  * image
1536  * 
1537  */
1538
1539
1540 /**
1541  * @class Roo.bootstrap.Link
1542  * @extends Roo.bootstrap.Component
1543  * Bootstrap Link Class
1544  * @cfg {String} alt image alternative text
1545  * @cfg {String} href a tag href
1546  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1547  * @cfg {String} html the content of the link.
1548  * @cfg {String} anchor name for the anchor link
1549  * @cfg {String} fa - favicon
1550
1551  * @cfg {Boolean} preventDefault (true | false) default false
1552
1553  * 
1554  * @constructor
1555  * Create a new Input
1556  * @param {Object} config The config object
1557  */
1558
1559 Roo.bootstrap.Link = function(config){
1560     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1561     
1562     this.addEvents({
1563         // img events
1564         /**
1565          * @event click
1566          * The img click event for the img.
1567          * @param {Roo.EventObject} e
1568          */
1569         "click" : true
1570     });
1571 };
1572
1573 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1574     
1575     href: false,
1576     target: false,
1577     preventDefault: false,
1578     anchor : false,
1579     alt : false,
1580     fa: false,
1581
1582
1583     getAutoCreate : function()
1584     {
1585         var html = this.html || '';
1586         
1587         if (this.fa !== false) {
1588             html = '<i class="fa fa-' + this.fa + '"></i>';
1589         }
1590         var cfg = {
1591             tag: 'a'
1592         };
1593         // anchor's do not require html/href...
1594         if (this.anchor === false) {
1595             cfg.html = html;
1596             cfg.href = this.href || '#';
1597         } else {
1598             cfg.name = this.anchor;
1599             if (this.html !== false || this.fa !== false) {
1600                 cfg.html = html;
1601             }
1602             if (this.href !== false) {
1603                 cfg.href = this.href;
1604             }
1605         }
1606         
1607         if(this.alt !== false){
1608             cfg.alt = this.alt;
1609         }
1610         
1611         
1612         if(this.target !== false) {
1613             cfg.target = this.target;
1614         }
1615         
1616         return cfg;
1617     },
1618     
1619     initEvents: function() {
1620         
1621         if(!this.href || this.preventDefault){
1622             this.el.on('click', this.onClick, this);
1623         }
1624     },
1625     
1626     onClick : function(e)
1627     {
1628         if(this.preventDefault){
1629             e.preventDefault();
1630         }
1631         //Roo.log('img onclick');
1632         this.fireEvent('click', this, e);
1633     }
1634    
1635 });
1636
1637  /*
1638  * - LGPL
1639  *
1640  * header
1641  * 
1642  */
1643
1644 /**
1645  * @class Roo.bootstrap.Header
1646  * @extends Roo.bootstrap.Component
1647  * Bootstrap Header class
1648  * @cfg {String} html content of header
1649  * @cfg {Number} level (1|2|3|4|5|6) default 1
1650  * 
1651  * @constructor
1652  * Create a new Header
1653  * @param {Object} config The config object
1654  */
1655
1656
1657 Roo.bootstrap.Header  = function(config){
1658     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1659 };
1660
1661 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1662     
1663     //href : false,
1664     html : false,
1665     level : 1,
1666     
1667     
1668     
1669     getAutoCreate : function(){
1670         
1671         
1672         
1673         var cfg = {
1674             tag: 'h' + (1 *this.level),
1675             html: this.html || ''
1676         } ;
1677         
1678         return cfg;
1679     }
1680    
1681 });
1682
1683  
1684
1685  /*
1686  * Based on:
1687  * Ext JS Library 1.1.1
1688  * Copyright(c) 2006-2007, Ext JS, LLC.
1689  *
1690  * Originally Released Under LGPL - original licence link has changed is not relivant.
1691  *
1692  * Fork - LGPL
1693  * <script type="text/javascript">
1694  */
1695  
1696 /**
1697  * @class Roo.bootstrap.MenuMgr
1698  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1699  * @singleton
1700  */
1701 Roo.bootstrap.MenuMgr = function(){
1702    var menus, active, groups = {}, attached = false, lastShow = new Date();
1703
1704    // private - called when first menu is created
1705    function init(){
1706        menus = {};
1707        active = new Roo.util.MixedCollection();
1708        Roo.get(document).addKeyListener(27, function(){
1709            if(active.length > 0){
1710                hideAll();
1711            }
1712        });
1713    }
1714
1715    // private
1716    function hideAll(){
1717        if(active && active.length > 0){
1718            var c = active.clone();
1719            c.each(function(m){
1720                m.hide();
1721            });
1722        }
1723    }
1724
1725    // private
1726    function onHide(m){
1727        active.remove(m);
1728        if(active.length < 1){
1729            Roo.get(document).un("mouseup", onMouseDown);
1730             
1731            attached = false;
1732        }
1733    }
1734
1735    // private
1736    function onShow(m){
1737        var last = active.last();
1738        lastShow = new Date();
1739        active.add(m);
1740        if(!attached){
1741           Roo.get(document).on("mouseup", onMouseDown);
1742            
1743            attached = true;
1744        }
1745        if(m.parentMenu){
1746           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1747           m.parentMenu.activeChild = m;
1748        }else if(last && last.isVisible()){
1749           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1750        }
1751    }
1752
1753    // private
1754    function onBeforeHide(m){
1755        if(m.activeChild){
1756            m.activeChild.hide();
1757        }
1758        if(m.autoHideTimer){
1759            clearTimeout(m.autoHideTimer);
1760            delete m.autoHideTimer;
1761        }
1762    }
1763
1764    // private
1765    function onBeforeShow(m){
1766        var pm = m.parentMenu;
1767        if(!pm && !m.allowOtherMenus){
1768            hideAll();
1769        }else if(pm && pm.activeChild && active != m){
1770            pm.activeChild.hide();
1771        }
1772    }
1773
1774    // private this should really trigger on mouseup..
1775    function onMouseDown(e){
1776         Roo.log("on Mouse Up");
1777         
1778         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1779             Roo.log("MenuManager hideAll");
1780             hideAll();
1781             e.stopEvent();
1782         }
1783         
1784         
1785    }
1786
1787    // private
1788    function onBeforeCheck(mi, state){
1789        if(state){
1790            var g = groups[mi.group];
1791            for(var i = 0, l = g.length; i < l; i++){
1792                if(g[i] != mi){
1793                    g[i].setChecked(false);
1794                }
1795            }
1796        }
1797    }
1798
1799    return {
1800
1801        /**
1802         * Hides all menus that are currently visible
1803         */
1804        hideAll : function(){
1805             hideAll();  
1806        },
1807
1808        // private
1809        register : function(menu){
1810            if(!menus){
1811                init();
1812            }
1813            menus[menu.id] = menu;
1814            menu.on("beforehide", onBeforeHide);
1815            menu.on("hide", onHide);
1816            menu.on("beforeshow", onBeforeShow);
1817            menu.on("show", onShow);
1818            var g = menu.group;
1819            if(g && menu.events["checkchange"]){
1820                if(!groups[g]){
1821                    groups[g] = [];
1822                }
1823                groups[g].push(menu);
1824                menu.on("checkchange", onCheck);
1825            }
1826        },
1827
1828         /**
1829          * Returns a {@link Roo.menu.Menu} object
1830          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1831          * be used to generate and return a new Menu instance.
1832          */
1833        get : function(menu){
1834            if(typeof menu == "string"){ // menu id
1835                return menus[menu];
1836            }else if(menu.events){  // menu instance
1837                return menu;
1838            }
1839            /*else if(typeof menu.length == 'number'){ // array of menu items?
1840                return new Roo.bootstrap.Menu({items:menu});
1841            }else{ // otherwise, must be a config
1842                return new Roo.bootstrap.Menu(menu);
1843            }
1844            */
1845            return false;
1846        },
1847
1848        // private
1849        unregister : function(menu){
1850            delete menus[menu.id];
1851            menu.un("beforehide", onBeforeHide);
1852            menu.un("hide", onHide);
1853            menu.un("beforeshow", onBeforeShow);
1854            menu.un("show", onShow);
1855            var g = menu.group;
1856            if(g && menu.events["checkchange"]){
1857                groups[g].remove(menu);
1858                menu.un("checkchange", onCheck);
1859            }
1860        },
1861
1862        // private
1863        registerCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                if(!groups[g]){
1867                    groups[g] = [];
1868                }
1869                groups[g].push(menuItem);
1870                menuItem.on("beforecheckchange", onBeforeCheck);
1871            }
1872        },
1873
1874        // private
1875        unregisterCheckable : function(menuItem){
1876            var g = menuItem.group;
1877            if(g){
1878                groups[g].remove(menuItem);
1879                menuItem.un("beforecheckchange", onBeforeCheck);
1880            }
1881        }
1882    };
1883 }();/*
1884  * - LGPL
1885  *
1886  * menu
1887  * 
1888  */
1889
1890 /**
1891  * @class Roo.bootstrap.Menu
1892  * @extends Roo.bootstrap.Component
1893  * Bootstrap Menu class - container for MenuItems
1894  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1895  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1896  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1897  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1898  * 
1899  * @constructor
1900  * Create a new Menu
1901  * @param {Object} config The config object
1902  */
1903
1904
1905 Roo.bootstrap.Menu = function(config){
1906     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1907     if (this.registerMenu && this.type != 'treeview')  {
1908         Roo.bootstrap.MenuMgr.register(this);
1909     }
1910     this.addEvents({
1911         /**
1912          * @event beforeshow
1913          * Fires before this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         beforeshow : true,
1917         /**
1918          * @event beforehide
1919          * Fires before this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         beforehide : true,
1923         /**
1924          * @event show
1925          * Fires after this menu is displayed
1926          * @param {Roo.menu.Menu} this
1927          */
1928         show : true,
1929         /**
1930          * @event hide
1931          * Fires after this menu is hidden
1932          * @param {Roo.menu.Menu} this
1933          */
1934         hide : true,
1935         /**
1936          * @event click
1937          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1938          * @param {Roo.menu.Menu} this
1939          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1940          * @param {Roo.EventObject} e
1941          */
1942         click : true,
1943         /**
1944          * @event mouseover
1945          * Fires when the mouse is hovering over this menu
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.EventObject} e
1948          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1949          */
1950         mouseover : true,
1951         /**
1952          * @event mouseout
1953          * Fires when the mouse exits this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseout : true,
1959         /**
1960          * @event itemclick
1961          * Fires when a menu item contained in this menu is clicked
1962          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1963          * @param {Roo.EventObject} e
1964          */
1965         itemclick: true
1966     });
1967     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1968 };
1969
1970 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1971     
1972    /// html : false,
1973     //align : '',
1974     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1975     type: false,
1976     /**
1977      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1978      */
1979     registerMenu : true,
1980     
1981     menuItems :false, // stores the menu items..
1982     
1983     hidden:true,
1984         
1985     parentMenu : false,
1986     
1987     stopEvent : true,
1988     
1989     isLink : false,
1990     
1991     getChildContainer : function() {
1992         return this.el;  
1993     },
1994     
1995     getAutoCreate : function(){
1996          
1997         //if (['right'].indexOf(this.align)!==-1) {
1998         //    cfg.cn[1].cls += ' pull-right'
1999         //}
2000         
2001         
2002         var cfg = {
2003             tag : 'ul',
2004             cls : 'dropdown-menu' ,
2005             style : 'z-index:1000'
2006             
2007         };
2008         
2009         if (this.type === 'submenu') {
2010             cfg.cls = 'submenu active';
2011         }
2012         if (this.type === 'treeview') {
2013             cfg.cls = 'treeview-menu';
2014         }
2015         
2016         return cfg;
2017     },
2018     initEvents : function() {
2019         
2020        // Roo.log("ADD event");
2021        // Roo.log(this.triggerEl.dom);
2022         
2023         this.triggerEl.on('click', this.onTriggerClick, this);
2024         
2025         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2026         
2027         this.triggerEl.addClass('dropdown-toggle');
2028         
2029         if (Roo.isTouch) {
2030             this.el.on('touchstart'  , this.onTouch, this);
2031         }
2032         this.el.on('click' , this.onClick, this);
2033
2034         this.el.on("mouseover", this.onMouseOver, this);
2035         this.el.on("mouseout", this.onMouseOut, this);
2036         
2037     },
2038     
2039     findTargetItem : function(e)
2040     {
2041         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2042         if(!t){
2043             return false;
2044         }
2045         //Roo.log(t);         Roo.log(t.id);
2046         if(t && t.id){
2047             //Roo.log(this.menuitems);
2048             return this.menuitems.get(t.id);
2049             
2050             //return this.items.get(t.menuItemId);
2051         }
2052         
2053         return false;
2054     },
2055     
2056     onTouch : function(e) 
2057     {
2058         Roo.log("menu.onTouch");
2059         //e.stopEvent(); this make the user popdown broken
2060         this.onClick(e);
2061     },
2062     
2063     onClick : function(e)
2064     {
2065         Roo.log("menu.onClick");
2066         
2067         var t = this.findTargetItem(e);
2068         if(!t || t.isContainer){
2069             return;
2070         }
2071         Roo.log(e);
2072         /*
2073         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2074             if(t == this.activeItem && t.shouldDeactivate(e)){
2075                 this.activeItem.deactivate();
2076                 delete this.activeItem;
2077                 return;
2078             }
2079             if(t.canActivate){
2080                 this.setActiveItem(t, true);
2081             }
2082             return;
2083             
2084             
2085         }
2086         */
2087        
2088         Roo.log('pass click event');
2089         
2090         t.onClick(e);
2091         
2092         this.fireEvent("click", this, t, e);
2093         
2094         var _this = this;
2095         
2096         (function() { _this.hide(); }).defer(500);
2097     },
2098     
2099     onMouseOver : function(e){
2100         var t  = this.findTargetItem(e);
2101         //Roo.log(t);
2102         //if(t){
2103         //    if(t.canActivate && !t.disabled){
2104         //        this.setActiveItem(t, true);
2105         //    }
2106         //}
2107         
2108         this.fireEvent("mouseover", this, e, t);
2109     },
2110     isVisible : function(){
2111         return !this.hidden;
2112     },
2113      onMouseOut : function(e){
2114         var t  = this.findTargetItem(e);
2115         
2116         //if(t ){
2117         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2118         //        this.activeItem.deactivate();
2119         //        delete this.activeItem;
2120         //    }
2121         //}
2122         this.fireEvent("mouseout", this, e, t);
2123     },
2124     
2125     
2126     /**
2127      * Displays this menu relative to another element
2128      * @param {String/HTMLElement/Roo.Element} element The element to align to
2129      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2130      * the element (defaults to this.defaultAlign)
2131      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2132      */
2133     show : function(el, pos, parentMenu){
2134         this.parentMenu = parentMenu;
2135         if(!this.el){
2136             this.render();
2137         }
2138         this.fireEvent("beforeshow", this);
2139         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2140     },
2141      /**
2142      * Displays this menu at a specific xy position
2143      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2144      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2145      */
2146     showAt : function(xy, parentMenu, /* private: */_e){
2147         this.parentMenu = parentMenu;
2148         if(!this.el){
2149             this.render();
2150         }
2151         if(_e !== false){
2152             this.fireEvent("beforeshow", this);
2153             //xy = this.el.adjustForConstraints(xy);
2154         }
2155         
2156         //this.el.show();
2157         this.hideMenuItems();
2158         this.hidden = false;
2159         this.triggerEl.addClass('open');
2160         
2161         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2162             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2163         }
2164         
2165         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2166             this.el.setXY(xy);
2167         }
2168         
2169         this.focus();
2170         this.fireEvent("show", this);
2171     },
2172     
2173     focus : function(){
2174         return;
2175         if(!this.hidden){
2176             this.doFocus.defer(50, this);
2177         }
2178     },
2179
2180     doFocus : function(){
2181         if(!this.hidden){
2182             this.focusEl.focus();
2183         }
2184     },
2185
2186     /**
2187      * Hides this menu and optionally all parent menus
2188      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2189      */
2190     hide : function(deep)
2191     {
2192         
2193         this.hideMenuItems();
2194         if(this.el && this.isVisible()){
2195             this.fireEvent("beforehide", this);
2196             if(this.activeItem){
2197                 this.activeItem.deactivate();
2198                 this.activeItem = null;
2199             }
2200             this.triggerEl.removeClass('open');;
2201             this.hidden = true;
2202             this.fireEvent("hide", this);
2203         }
2204         if(deep === true && this.parentMenu){
2205             this.parentMenu.hide(true);
2206         }
2207     },
2208     
2209     onTriggerClick : function(e)
2210     {
2211         Roo.log('trigger click');
2212         
2213         var target = e.getTarget();
2214         
2215         Roo.log(target.nodeName.toLowerCase());
2216         
2217         if(target.nodeName.toLowerCase() === 'i'){
2218             e.preventDefault();
2219         }
2220         
2221     },
2222     
2223     onTriggerPress  : function(e)
2224     {
2225         Roo.log('trigger press');
2226         //Roo.log(e.getTarget());
2227        // Roo.log(this.triggerEl.dom);
2228        
2229         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2230         var pel = Roo.get(e.getTarget());
2231         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2232             Roo.log('is treeview or dropdown?');
2233             return;
2234         }
2235         
2236         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2237             return;
2238         }
2239         
2240         if (this.isVisible()) {
2241             Roo.log('hide');
2242             this.hide();
2243         } else {
2244             Roo.log('show');
2245             this.show(this.triggerEl, false, false);
2246         }
2247         
2248         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2249             e.stopEvent();
2250         }
2251         
2252     },
2253        
2254     
2255     hideMenuItems : function()
2256     {
2257         Roo.log("hide Menu Items");
2258         if (!this.el) { 
2259             return;
2260         }
2261         //$(backdrop).remove()
2262         this.el.select('.open',true).each(function(aa) {
2263             
2264             aa.removeClass('open');
2265           //var parent = getParent($(this))
2266           //var relatedTarget = { relatedTarget: this }
2267           
2268            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2269           //if (e.isDefaultPrevented()) return
2270            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2271         });
2272     },
2273     addxtypeChild : function (tree, cntr) {
2274         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2275           
2276         this.menuitems.add(comp);
2277         return comp;
2278
2279     },
2280     getEl : function()
2281     {
2282         Roo.log(this.el);
2283         return this.el;
2284     }
2285 });
2286
2287  
2288  /*
2289  * - LGPL
2290  *
2291  * menu item
2292  * 
2293  */
2294
2295
2296 /**
2297  * @class Roo.bootstrap.MenuItem
2298  * @extends Roo.bootstrap.Component
2299  * Bootstrap MenuItem class
2300  * @cfg {String} html the menu label
2301  * @cfg {String} href the link
2302  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2303  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2304  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2305  * @cfg {String} fa favicon to show on left of menu item.
2306  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2307  * 
2308  * 
2309  * @constructor
2310  * Create a new MenuItem
2311  * @param {Object} config The config object
2312  */
2313
2314
2315 Roo.bootstrap.MenuItem = function(config){
2316     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2317     this.addEvents({
2318         // raw events
2319         /**
2320          * @event click
2321          * The raw click event for the entire grid.
2322          * @param {Roo.bootstrap.MenuItem} this
2323          * @param {Roo.EventObject} e
2324          */
2325         "click" : true
2326     });
2327 };
2328
2329 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2330     
2331     href : false,
2332     html : false,
2333     preventDefault: true,
2334     isContainer : false,
2335     active : false,
2336     fa: false,
2337     
2338     getAutoCreate : function(){
2339         
2340         if(this.isContainer){
2341             return {
2342                 tag: 'li',
2343                 cls: 'dropdown-menu-item'
2344             };
2345         }
2346         var ctag = {
2347             tag: 'span',
2348             html: 'Link'
2349         };
2350         
2351         var anc = {
2352             tag : 'a',
2353             href : '#',
2354             cn : [  ]
2355         };
2356         
2357         if (this.fa !== false) {
2358             anc.cn.push({
2359                 tag : 'i',
2360                 cls : 'fa fa-' + this.fa
2361             });
2362         }
2363         
2364         anc.cn.push(ctag);
2365         
2366         
2367         var cfg= {
2368             tag: 'li',
2369             cls: 'dropdown-menu-item',
2370             cn: [ anc ]
2371         };
2372         if (this.parent().type == 'treeview') {
2373             cfg.cls = 'treeview-menu';
2374         }
2375         if (this.active) {
2376             cfg.cls += ' active';
2377         }
2378         
2379         
2380         
2381         anc.href = this.href || cfg.cn[0].href ;
2382         ctag.html = this.html || cfg.cn[0].html ;
2383         return cfg;
2384     },
2385     
2386     initEvents: function()
2387     {
2388         if (this.parent().type == 'treeview') {
2389             this.el.select('a').on('click', this.onClick, this);
2390         }
2391         if (this.menu) {
2392             this.menu.parentType = this.xtype;
2393             this.menu.triggerEl = this.el;
2394             this.menu = this.addxtype(Roo.apply({}, this.menu));
2395         }
2396         
2397     },
2398     onClick : function(e)
2399     {
2400         Roo.log('item on click ');
2401         //if(this.preventDefault){
2402         //    e.preventDefault();
2403         //}
2404         //this.parent().hideMenuItems();
2405         
2406         this.fireEvent('click', this, e);
2407     },
2408     getEl : function()
2409     {
2410         return this.el;
2411     } 
2412 });
2413
2414  
2415
2416  /*
2417  * - LGPL
2418  *
2419  * menu separator
2420  * 
2421  */
2422
2423
2424 /**
2425  * @class Roo.bootstrap.MenuSeparator
2426  * @extends Roo.bootstrap.Component
2427  * Bootstrap MenuSeparator class
2428  * 
2429  * @constructor
2430  * Create a new MenuItem
2431  * @param {Object} config The config object
2432  */
2433
2434
2435 Roo.bootstrap.MenuSeparator = function(config){
2436     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2437 };
2438
2439 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2440     
2441     getAutoCreate : function(){
2442         var cfg = {
2443             cls: 'divider',
2444             tag : 'li'
2445         };
2446         
2447         return cfg;
2448     }
2449    
2450 });
2451
2452  
2453
2454  
2455 /*
2456 * Licence: LGPL
2457 */
2458
2459 /**
2460  * @class Roo.bootstrap.Modal
2461  * @extends Roo.bootstrap.Component
2462  * Bootstrap Modal class
2463  * @cfg {String} title Title of dialog
2464  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2465  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2466  * @cfg {Boolean} specificTitle default false
2467  * @cfg {Array} buttons Array of buttons or standard button set..
2468  * @cfg {String} buttonPosition (left|right|center) default right
2469  * @cfg {Boolean} animate default true
2470  * @cfg {Boolean} allow_close default true
2471  * @cfg {Boolean} fitwindow default false
2472  * @cfg {String} size (sm|lg) default empty
2473  * 
2474  * 
2475  * @constructor
2476  * Create a new Modal Dialog
2477  * @param {Object} config The config object
2478  */
2479
2480 Roo.bootstrap.Modal = function(config){
2481     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2482     this.addEvents({
2483         // raw events
2484         /**
2485          * @event btnclick
2486          * The raw btnclick event for the button
2487          * @param {Roo.EventObject} e
2488          */
2489         "btnclick" : true
2490     });
2491     this.buttons = this.buttons || [];
2492      
2493     if (this.tmpl) {
2494         this.tmpl = Roo.factory(this.tmpl);
2495     }
2496     
2497 };
2498
2499 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2500     
2501     title : 'test dialog',
2502    
2503     buttons : false,
2504     
2505     // set on load...
2506      
2507     html: false,
2508     
2509     tmp: false,
2510     
2511     specificTitle: false,
2512     
2513     buttonPosition: 'right',
2514     
2515     allow_close : true,
2516     
2517     animate : true,
2518     
2519     fitwindow: false,
2520     
2521     
2522      // private
2523     dialogEl: false,
2524     bodyEl:  false,
2525     footerEl:  false,
2526     titleEl:  false,
2527     closeEl:  false,
2528     
2529     size: '',
2530     
2531     
2532     onRender : function(ct, position)
2533     {
2534         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2535      
2536         if(!this.el){
2537             var cfg = Roo.apply({},  this.getAutoCreate());
2538             cfg.id = Roo.id();
2539             //if(!cfg.name){
2540             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2541             //}
2542             //if (!cfg.name.length) {
2543             //    delete cfg.name;
2544            // }
2545             if (this.cls) {
2546                 cfg.cls += ' ' + this.cls;
2547             }
2548             if (this.style) {
2549                 cfg.style = this.style;
2550             }
2551             this.el = Roo.get(document.body).createChild(cfg, position);
2552         }
2553         //var type = this.el.dom.type;
2554         
2555         
2556         if(this.tabIndex !== undefined){
2557             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2558         }
2559         
2560         this.dialogEl = this.el.select('.modal-dialog',true).first();
2561         this.bodyEl = this.el.select('.modal-body',true).first();
2562         this.closeEl = this.el.select('.modal-header .close', true).first();
2563         this.footerEl = this.el.select('.modal-footer',true).first();
2564         this.titleEl = this.el.select('.modal-title',true).first();
2565         
2566         
2567          
2568         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2569         this.maskEl.enableDisplayMode("block");
2570         this.maskEl.hide();
2571         //this.el.addClass("x-dlg-modal");
2572     
2573         if (this.buttons.length) {
2574             Roo.each(this.buttons, function(bb) {
2575                 var b = Roo.apply({}, bb);
2576                 b.xns = b.xns || Roo.bootstrap;
2577                 b.xtype = b.xtype || 'Button';
2578                 if (typeof(b.listeners) == 'undefined') {
2579                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2580                 }
2581                 
2582                 var btn = Roo.factory(b);
2583                 
2584                 btn.render(this.el.select('.modal-footer div').first());
2585                 
2586             },this);
2587         }
2588         // render the children.
2589         var nitems = [];
2590         
2591         if(typeof(this.items) != 'undefined'){
2592             var items = this.items;
2593             delete this.items;
2594
2595             for(var i =0;i < items.length;i++) {
2596                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2597             }
2598         }
2599         
2600         this.items = nitems;
2601         
2602         // where are these used - they used to be body/close/footer
2603         
2604        
2605         this.initEvents();
2606         //this.el.addClass([this.fieldClass, this.cls]);
2607         
2608     },
2609     
2610     getAutoCreate : function(){
2611         
2612         
2613         var bdy = {
2614                 cls : 'modal-body',
2615                 html : this.html || ''
2616         };
2617         
2618         var title = {
2619             tag: 'h4',
2620             cls : 'modal-title',
2621             html : this.title
2622         };
2623         
2624         if(this.specificTitle){
2625             title = this.title;
2626             
2627         };
2628         
2629         var header = [];
2630         if (this.allow_close) {
2631             header.push({
2632                 tag: 'button',
2633                 cls : 'close',
2634                 html : '&times'
2635             });
2636         }
2637         
2638         header.push(title);
2639         
2640         var size = '';
2641         
2642         if(this.size.length){
2643             size = 'modal-' + this.size;
2644         }
2645         
2646         var modal = {
2647             cls: "modal",
2648             style : 'display: none',
2649             cn : [
2650                 {
2651                     cls: "modal-dialog " + size,
2652                     cn : [
2653                         {
2654                             cls : "modal-content",
2655                             cn : [
2656                                 {
2657                                     cls : 'modal-header',
2658                                     cn : header
2659                                 },
2660                                 bdy,
2661                                 {
2662                                     cls : 'modal-footer',
2663                                     cn : [
2664                                         {
2665                                             tag: 'div',
2666                                             cls: 'btn-' + this.buttonPosition
2667                                         }
2668                                     ]
2669                                     
2670                                 }
2671                                 
2672                                 
2673                             ]
2674                             
2675                         }
2676                     ]
2677                         
2678                 }
2679             ]
2680         };
2681         
2682         if(this.animate){
2683             modal.cls += ' fade';
2684         }
2685         
2686         return modal;
2687           
2688     },
2689     getChildContainer : function() {
2690          
2691          return this.bodyEl;
2692         
2693     },
2694     getButtonContainer : function() {
2695          return this.el.select('.modal-footer div',true).first();
2696         
2697     },
2698     initEvents : function()
2699     {
2700         if (this.allow_close) {
2701             this.closeEl.on('click', this.hide, this);
2702         }
2703         Roo.EventManager.onWindowResize(this.resize, this, true);
2704         
2705  
2706     },
2707     
2708     resize : function()
2709     {
2710         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2711         if (this.fitwindow) {
2712             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2713             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2714             this.setSize(w,h);
2715         }
2716     },
2717     
2718     setSize : function(w,h)
2719     {
2720         if (!w && !h) {
2721             return;
2722         }
2723         this.resizeTo(w,h);
2724     },
2725     
2726     show : function() {
2727         
2728         if (!this.rendered) {
2729             this.render();
2730         }
2731         
2732         this.el.setStyle('display', 'block');
2733         
2734         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2735             var _this = this;
2736             (function(){
2737                 this.el.addClass('in');
2738             }).defer(50, this);
2739         }else{
2740             this.el.addClass('in');
2741             
2742         }
2743         
2744         // not sure how we can show data in here.. 
2745         //if (this.tmpl) {
2746         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2747         //}
2748         
2749         Roo.get(document.body).addClass("x-body-masked");
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2751         this.maskEl.show();
2752         this.el.setStyle('zIndex', '10001');
2753        
2754         this.fireEvent('show', this);
2755         
2756         this.resize();
2757         
2758         (function () {
2759             this.items.forEach( function(e) {
2760                 e.layout ? e.layout() : false;
2761                     
2762             });
2763         }).defer(100,this);
2764         
2765     },
2766     hide : function()
2767     {
2768         this.maskEl.hide();
2769         Roo.get(document.body).removeClass("x-body-masked");
2770         this.el.removeClass('in');
2771         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2772         
2773         if(this.animate){ // why
2774             var _this = this;
2775             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2776         }else{
2777             this.el.setStyle('display', 'none');
2778         }
2779         
2780         this.fireEvent('hide', this);
2781     },
2782     
2783     addButton : function(str, cb)
2784     {
2785          
2786         
2787         var b = Roo.apply({}, { html : str } );
2788         b.xns = b.xns || Roo.bootstrap;
2789         b.xtype = b.xtype || 'Button';
2790         if (typeof(b.listeners) == 'undefined') {
2791             b.listeners = { click : cb.createDelegate(this)  };
2792         }
2793         
2794         var btn = Roo.factory(b);
2795            
2796         btn.render(this.el.select('.modal-footer div').first());
2797         
2798         return btn;   
2799        
2800     },
2801     
2802     setDefaultButton : function(btn)
2803     {
2804         //this.el.select('.modal-footer').()
2805     },
2806     diff : false,
2807     
2808     resizeTo: function(w,h)
2809     {
2810         // skip.. ?? why??
2811         
2812         this.dialogEl.setWidth(w);
2813         if (this.diff === false) {
2814             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2815         }
2816         
2817         this.bodyEl.setHeight(h-this.diff);
2818         
2819         
2820     },
2821     setContentSize  : function(w, h)
2822     {
2823         
2824     },
2825     onButtonClick: function(btn,e)
2826     {
2827         //Roo.log([a,b,c]);
2828         this.fireEvent('btnclick', btn.name, e);
2829     },
2830      /**
2831      * Set the title of the Dialog
2832      * @param {String} str new Title
2833      */
2834     setTitle: function(str) {
2835         this.titleEl.dom.innerHTML = str;    
2836     },
2837     /**
2838      * Set the body of the Dialog
2839      * @param {String} str new Title
2840      */
2841     setBody: function(str) {
2842         this.bodyEl.dom.innerHTML = str;    
2843     },
2844     /**
2845      * Set the body of the Dialog using the template
2846      * @param {Obj} data - apply this data to the template and replace the body contents.
2847      */
2848     applyBody: function(obj)
2849     {
2850         if (!this.tmpl) {
2851             Roo.log("Error - using apply Body without a template");
2852             //code
2853         }
2854         this.tmpl.overwrite(this.bodyEl, obj);
2855     }
2856     
2857 });
2858
2859
2860 Roo.apply(Roo.bootstrap.Modal,  {
2861     /**
2862          * Button config that displays a single OK button
2863          * @type Object
2864          */
2865         OK :  [{
2866             name : 'ok',
2867             weight : 'primary',
2868             html : 'OK'
2869         }], 
2870         /**
2871          * Button config that displays Yes and No buttons
2872          * @type Object
2873          */
2874         YESNO : [
2875             {
2876                 name  : 'no',
2877                 html : 'No'
2878             },
2879             {
2880                 name  :'yes',
2881                 weight : 'primary',
2882                 html : 'Yes'
2883             }
2884         ],
2885         
2886         /**
2887          * Button config that displays OK and Cancel buttons
2888          * @type Object
2889          */
2890         OKCANCEL : [
2891             {
2892                name : 'cancel',
2893                 html : 'Cancel'
2894             },
2895             {
2896                 name : 'ok',
2897                 weight : 'primary',
2898                 html : 'OK'
2899             }
2900         ],
2901         /**
2902          * Button config that displays Yes, No and Cancel buttons
2903          * @type Object
2904          */
2905         YESNOCANCEL : [
2906             {
2907                 name : 'yes',
2908                 weight : 'primary',
2909                 html : 'Yes'
2910             },
2911             {
2912                 name : 'no',
2913                 html : 'No'
2914             },
2915             {
2916                 name : 'cancel',
2917                 html : 'Cancel'
2918             }
2919         ]
2920 });
2921  
2922  /*
2923  * - LGPL
2924  *
2925  * messagebox - can be used as a replace
2926  * 
2927  */
2928 /**
2929  * @class Roo.MessageBox
2930  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2931  * Example usage:
2932  *<pre><code>
2933 // Basic alert:
2934 Roo.Msg.alert('Status', 'Changes saved successfully.');
2935
2936 // Prompt for user data:
2937 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2938     if (btn == 'ok'){
2939         // process text value...
2940     }
2941 });
2942
2943 // Show a dialog using config options:
2944 Roo.Msg.show({
2945    title:'Save Changes?',
2946    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2947    buttons: Roo.Msg.YESNOCANCEL,
2948    fn: processResult,
2949    animEl: 'elId'
2950 });
2951 </code></pre>
2952  * @singleton
2953  */
2954 Roo.bootstrap.MessageBox = function(){
2955     var dlg, opt, mask, waitTimer;
2956     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2957     var buttons, activeTextEl, bwidth;
2958
2959     
2960     // private
2961     var handleButton = function(button){
2962         dlg.hide();
2963         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2964     };
2965
2966     // private
2967     var handleHide = function(){
2968         if(opt && opt.cls){
2969             dlg.el.removeClass(opt.cls);
2970         }
2971         //if(waitTimer){
2972         //    Roo.TaskMgr.stop(waitTimer);
2973         //    waitTimer = null;
2974         //}
2975     };
2976
2977     // private
2978     var updateButtons = function(b){
2979         var width = 0;
2980         if(!b){
2981             buttons["ok"].hide();
2982             buttons["cancel"].hide();
2983             buttons["yes"].hide();
2984             buttons["no"].hide();
2985             //dlg.footer.dom.style.display = 'none';
2986             return width;
2987         }
2988         dlg.footerEl.dom.style.display = '';
2989         for(var k in buttons){
2990             if(typeof buttons[k] != "function"){
2991                 if(b[k]){
2992                     buttons[k].show();
2993                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2994                     width += buttons[k].el.getWidth()+15;
2995                 }else{
2996                     buttons[k].hide();
2997                 }
2998             }
2999         }
3000         return width;
3001     };
3002
3003     // private
3004     var handleEsc = function(d, k, e){
3005         if(opt && opt.closable !== false){
3006             dlg.hide();
3007         }
3008         if(e){
3009             e.stopEvent();
3010         }
3011     };
3012
3013     return {
3014         /**
3015          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3016          * @return {Roo.BasicDialog} The BasicDialog element
3017          */
3018         getDialog : function(){
3019            if(!dlg){
3020                 dlg = new Roo.bootstrap.Modal( {
3021                     //draggable: true,
3022                     //resizable:false,
3023                     //constraintoviewport:false,
3024                     //fixedcenter:true,
3025                     //collapsible : false,
3026                     //shim:true,
3027                     //modal: true,
3028                   //  width:400,
3029                   //  height:100,
3030                     //buttonAlign:"center",
3031                     closeClick : function(){
3032                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3033                             handleButton("no");
3034                         }else{
3035                             handleButton("cancel");
3036                         }
3037                     }
3038                 });
3039                 dlg.render();
3040                 dlg.on("hide", handleHide);
3041                 mask = dlg.mask;
3042                 //dlg.addKeyListener(27, handleEsc);
3043                 buttons = {};
3044                 this.buttons = buttons;
3045                 var bt = this.buttonText;
3046                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3047                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3048                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3049                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3050                 //Roo.log(buttons);
3051                 bodyEl = dlg.bodyEl.createChild({
3052
3053                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3054                         '<textarea class="roo-mb-textarea"></textarea>' +
3055                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3056                 });
3057                 msgEl = bodyEl.dom.firstChild;
3058                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3059                 textboxEl.enableDisplayMode();
3060                 textboxEl.addKeyListener([10,13], function(){
3061                     if(dlg.isVisible() && opt && opt.buttons){
3062                         if(opt.buttons.ok){
3063                             handleButton("ok");
3064                         }else if(opt.buttons.yes){
3065                             handleButton("yes");
3066                         }
3067                     }
3068                 });
3069                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3070                 textareaEl.enableDisplayMode();
3071                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3072                 progressEl.enableDisplayMode();
3073                 var pf = progressEl.dom.firstChild;
3074                 if (pf) {
3075                     pp = Roo.get(pf.firstChild);
3076                     pp.setHeight(pf.offsetHeight);
3077                 }
3078                 
3079             }
3080             return dlg;
3081         },
3082
3083         /**
3084          * Updates the message box body text
3085          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3086          * the XHTML-compliant non-breaking space character '&amp;#160;')
3087          * @return {Roo.MessageBox} This message box
3088          */
3089         updateText : function(text){
3090             if(!dlg.isVisible() && !opt.width){
3091                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3092             }
3093             msgEl.innerHTML = text || '&#160;';
3094       
3095             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3096             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3097             var w = Math.max(
3098                     Math.min(opt.width || cw , this.maxWidth), 
3099                     Math.max(opt.minWidth || this.minWidth, bwidth)
3100             );
3101             if(opt.prompt){
3102                 activeTextEl.setWidth(w);
3103             }
3104             if(dlg.isVisible()){
3105                 dlg.fixedcenter = false;
3106             }
3107             // to big, make it scroll. = But as usual stupid IE does not support
3108             // !important..
3109             
3110             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3111                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3112                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3113             } else {
3114                 bodyEl.dom.style.height = '';
3115                 bodyEl.dom.style.overflowY = '';
3116             }
3117             if (cw > w) {
3118                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3119             } else {
3120                 bodyEl.dom.style.overflowX = '';
3121             }
3122             
3123             dlg.setContentSize(w, bodyEl.getHeight());
3124             if(dlg.isVisible()){
3125                 dlg.fixedcenter = true;
3126             }
3127             return this;
3128         },
3129
3130         /**
3131          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3132          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3133          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3134          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3135          * @return {Roo.MessageBox} This message box
3136          */
3137         updateProgress : function(value, text){
3138             if(text){
3139                 this.updateText(text);
3140             }
3141             if (pp) { // weird bug on my firefox - for some reason this is not defined
3142                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3143             }
3144             return this;
3145         },        
3146
3147         /**
3148          * Returns true if the message box is currently displayed
3149          * @return {Boolean} True if the message box is visible, else false
3150          */
3151         isVisible : function(){
3152             return dlg && dlg.isVisible();  
3153         },
3154
3155         /**
3156          * Hides the message box if it is displayed
3157          */
3158         hide : function(){
3159             if(this.isVisible()){
3160                 dlg.hide();
3161             }  
3162         },
3163
3164         /**
3165          * Displays a new message box, or reinitializes an existing message box, based on the config options
3166          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3167          * The following config object properties are supported:
3168          * <pre>
3169 Property    Type             Description
3170 ----------  ---------------  ------------------------------------------------------------------------------------
3171 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3172                                    closes (defaults to undefined)
3173 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3174                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3175 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3176                                    progress and wait dialogs will ignore this property and always hide the
3177                                    close button as they can only be closed programmatically.
3178 cls               String           A custom CSS class to apply to the message box element
3179 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3180                                    displayed (defaults to 75)
3181 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3182                                    function will be btn (the name of the button that was clicked, if applicable,
3183                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3184                                    Progress and wait dialogs will ignore this option since they do not respond to
3185                                    user actions and can only be closed programmatically, so any required function
3186                                    should be called by the same code after it closes the dialog.
3187 icon              String           A CSS class that provides a background image to be used as an icon for
3188                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3189 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3190 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3191 modal             Boolean          False to allow user interaction with the page while the message box is
3192                                    displayed (defaults to true)
3193 msg               String           A string that will replace the existing message box body text (defaults
3194                                    to the XHTML-compliant non-breaking space character '&#160;')
3195 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3196 progress          Boolean          True to display a progress bar (defaults to false)
3197 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3198 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3199 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3200 title             String           The title text
3201 value             String           The string value to set into the active textbox element if displayed
3202 wait              Boolean          True to display a progress bar (defaults to false)
3203 width             Number           The width of the dialog in pixels
3204 </pre>
3205          *
3206          * Example usage:
3207          * <pre><code>
3208 Roo.Msg.show({
3209    title: 'Address',
3210    msg: 'Please enter your address:',
3211    width: 300,
3212    buttons: Roo.MessageBox.OKCANCEL,
3213    multiline: true,
3214    fn: saveAddress,
3215    animEl: 'addAddressBtn'
3216 });
3217 </code></pre>
3218          * @param {Object} config Configuration options
3219          * @return {Roo.MessageBox} This message box
3220          */
3221         show : function(options)
3222         {
3223             
3224             // this causes nightmares if you show one dialog after another
3225             // especially on callbacks..
3226              
3227             if(this.isVisible()){
3228                 
3229                 this.hide();
3230                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3231                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3232                 Roo.log("New Dialog Message:" +  options.msg )
3233                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3234                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3235                 
3236             }
3237             var d = this.getDialog();
3238             opt = options;
3239             d.setTitle(opt.title || "&#160;");
3240             d.closeEl.setDisplayed(opt.closable !== false);
3241             activeTextEl = textboxEl;
3242             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3243             if(opt.prompt){
3244                 if(opt.multiline){
3245                     textboxEl.hide();
3246                     textareaEl.show();
3247                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3248                         opt.multiline : this.defaultTextHeight);
3249                     activeTextEl = textareaEl;
3250                 }else{
3251                     textboxEl.show();
3252                     textareaEl.hide();
3253                 }
3254             }else{
3255                 textboxEl.hide();
3256                 textareaEl.hide();
3257             }
3258             progressEl.setDisplayed(opt.progress === true);
3259             this.updateProgress(0);
3260             activeTextEl.dom.value = opt.value || "";
3261             if(opt.prompt){
3262                 dlg.setDefaultButton(activeTextEl);
3263             }else{
3264                 var bs = opt.buttons;
3265                 var db = null;
3266                 if(bs && bs.ok){
3267                     db = buttons["ok"];
3268                 }else if(bs && bs.yes){
3269                     db = buttons["yes"];
3270                 }
3271                 dlg.setDefaultButton(db);
3272             }
3273             bwidth = updateButtons(opt.buttons);
3274             this.updateText(opt.msg);
3275             if(opt.cls){
3276                 d.el.addClass(opt.cls);
3277             }
3278             d.proxyDrag = opt.proxyDrag === true;
3279             d.modal = opt.modal !== false;
3280             d.mask = opt.modal !== false ? mask : false;
3281             if(!d.isVisible()){
3282                 // force it to the end of the z-index stack so it gets a cursor in FF
3283                 document.body.appendChild(dlg.el.dom);
3284                 d.animateTarget = null;
3285                 d.show(options.animEl);
3286             }
3287             return this;
3288         },
3289
3290         /**
3291          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3292          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3293          * and closing the message box when the process is complete.
3294          * @param {String} title The title bar text
3295          * @param {String} msg The message box body text
3296          * @return {Roo.MessageBox} This message box
3297          */
3298         progress : function(title, msg){
3299             this.show({
3300                 title : title,
3301                 msg : msg,
3302                 buttons: false,
3303                 progress:true,
3304                 closable:false,
3305                 minWidth: this.minProgressWidth,
3306                 modal : true
3307             });
3308             return this;
3309         },
3310
3311         /**
3312          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3313          * If a callback function is passed it will be called after the user clicks the button, and the
3314          * id of the button that was clicked will be passed as the only parameter to the callback
3315          * (could also be the top-right close button).
3316          * @param {String} title The title bar text
3317          * @param {String} msg The message box body text
3318          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3319          * @param {Object} scope (optional) The scope of the callback function
3320          * @return {Roo.MessageBox} This message box
3321          */
3322         alert : function(title, msg, fn, scope){
3323             this.show({
3324                 title : title,
3325                 msg : msg,
3326                 buttons: this.OK,
3327                 fn: fn,
3328                 scope : scope,
3329                 modal : true
3330             });
3331             return this;
3332         },
3333
3334         /**
3335          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3336          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3337          * You are responsible for closing the message box when the process is complete.
3338          * @param {String} msg The message box body text
3339          * @param {String} title (optional) The title bar text
3340          * @return {Roo.MessageBox} This message box
3341          */
3342         wait : function(msg, title){
3343             this.show({
3344                 title : title,
3345                 msg : msg,
3346                 buttons: false,
3347                 closable:false,
3348                 progress:true,
3349                 modal:true,
3350                 width:300,
3351                 wait:true
3352             });
3353             waitTimer = Roo.TaskMgr.start({
3354                 run: function(i){
3355                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3356                 },
3357                 interval: 1000
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3364          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3365          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3366          * @param {String} title The title bar text
3367          * @param {String} msg The message box body text
3368          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3369          * @param {Object} scope (optional) The scope of the callback function
3370          * @return {Roo.MessageBox} This message box
3371          */
3372         confirm : function(title, msg, fn, scope){
3373             this.show({
3374                 title : title,
3375                 msg : msg,
3376                 buttons: this.YESNO,
3377                 fn: fn,
3378                 scope : scope,
3379                 modal : true
3380             });
3381             return this;
3382         },
3383
3384         /**
3385          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3386          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3387          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3388          * (could also be the top-right close button) and the text that was entered will be passed as the two
3389          * parameters to the callback.
3390          * @param {String} title The title bar text
3391          * @param {String} msg The message box body text
3392          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3393          * @param {Object} scope (optional) The scope of the callback function
3394          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3395          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3396          * @return {Roo.MessageBox} This message box
3397          */
3398         prompt : function(title, msg, fn, scope, multiline){
3399             this.show({
3400                 title : title,
3401                 msg : msg,
3402                 buttons: this.OKCANCEL,
3403                 fn: fn,
3404                 minWidth:250,
3405                 scope : scope,
3406                 prompt:true,
3407                 multiline: multiline,
3408                 modal : true
3409             });
3410             return this;
3411         },
3412
3413         /**
3414          * Button config that displays a single OK button
3415          * @type Object
3416          */
3417         OK : {ok:true},
3418         /**
3419          * Button config that displays Yes and No buttons
3420          * @type Object
3421          */
3422         YESNO : {yes:true, no:true},
3423         /**
3424          * Button config that displays OK and Cancel buttons
3425          * @type Object
3426          */
3427         OKCANCEL : {ok:true, cancel:true},
3428         /**
3429          * Button config that displays Yes, No and Cancel buttons
3430          * @type Object
3431          */
3432         YESNOCANCEL : {yes:true, no:true, cancel:true},
3433
3434         /**
3435          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3436          * @type Number
3437          */
3438         defaultTextHeight : 75,
3439         /**
3440          * The maximum width in pixels of the message box (defaults to 600)
3441          * @type Number
3442          */
3443         maxWidth : 600,
3444         /**
3445          * The minimum width in pixels of the message box (defaults to 100)
3446          * @type Number
3447          */
3448         minWidth : 100,
3449         /**
3450          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3451          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3452          * @type Number
3453          */
3454         minProgressWidth : 250,
3455         /**
3456          * An object containing the default button text strings that can be overriden for localized language support.
3457          * Supported properties are: ok, cancel, yes and no.
3458          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3459          * @type Object
3460          */
3461         buttonText : {
3462             ok : "OK",
3463             cancel : "Cancel",
3464             yes : "Yes",
3465             no : "No"
3466         }
3467     };
3468 }();
3469
3470 /**
3471  * Shorthand for {@link Roo.MessageBox}
3472  */
3473 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3474 Roo.Msg = Roo.Msg || Roo.MessageBox;
3475 /*
3476  * - LGPL
3477  *
3478  * navbar
3479  * 
3480  */
3481
3482 /**
3483  * @class Roo.bootstrap.Navbar
3484  * @extends Roo.bootstrap.Component
3485  * Bootstrap Navbar class
3486
3487  * @constructor
3488  * Create a new Navbar
3489  * @param {Object} config The config object
3490  */
3491
3492
3493 Roo.bootstrap.Navbar = function(config){
3494     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3495     this.addEvents({
3496         // raw events
3497         /**
3498          * @event beforetoggle
3499          * Fire before toggle the menu
3500          * @param {Roo.EventObject} e
3501          */
3502         "beforetoggle" : true
3503     });
3504 };
3505
3506 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3507     
3508     
3509    
3510     // private
3511     navItems : false,
3512     loadMask : false,
3513     
3514     
3515     getAutoCreate : function(){
3516         
3517         
3518         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3519         
3520     },
3521     
3522     initEvents :function ()
3523     {
3524         //Roo.log(this.el.select('.navbar-toggle',true));
3525         this.el.select('.navbar-toggle',true).on('click', function() {
3526             if(this.fireEvent('beforetoggle', this) !== false){
3527                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3528             }
3529             
3530         }, this);
3531         
3532         var mark = {
3533             tag: "div",
3534             cls:"x-dlg-mask"
3535         };
3536         
3537         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3538         
3539         var size = this.el.getSize();
3540         this.maskEl.setSize(size.width, size.height);
3541         this.maskEl.enableDisplayMode("block");
3542         this.maskEl.hide();
3543         
3544         if(this.loadMask){
3545             this.maskEl.show();
3546         }
3547     },
3548     
3549     
3550     getChildContainer : function()
3551     {
3552         if (this.el.select('.collapse').getCount()) {
3553             return this.el.select('.collapse',true).first();
3554         }
3555         
3556         return this.el;
3557     },
3558     
3559     mask : function()
3560     {
3561         this.maskEl.show();
3562     },
3563     
3564     unmask : function()
3565     {
3566         this.maskEl.hide();
3567     } 
3568     
3569     
3570     
3571     
3572 });
3573
3574
3575
3576  
3577
3578  /*
3579  * - LGPL
3580  *
3581  * navbar
3582  * 
3583  */
3584
3585 /**
3586  * @class Roo.bootstrap.NavSimplebar
3587  * @extends Roo.bootstrap.Navbar
3588  * Bootstrap Sidebar class
3589  *
3590  * @cfg {Boolean} inverse is inverted color
3591  * 
3592  * @cfg {String} type (nav | pills | tabs)
3593  * @cfg {Boolean} arrangement stacked | justified
3594  * @cfg {String} align (left | right) alignment
3595  * 
3596  * @cfg {Boolean} main (true|false) main nav bar? default false
3597  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3598  * 
3599  * @cfg {String} tag (header|footer|nav|div) default is nav 
3600
3601  * 
3602  * 
3603  * 
3604  * @constructor
3605  * Create a new Sidebar
3606  * @param {Object} config The config object
3607  */
3608
3609
3610 Roo.bootstrap.NavSimplebar = function(config){
3611     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3612 };
3613
3614 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3615     
3616     inverse: false,
3617     
3618     type: false,
3619     arrangement: '',
3620     align : false,
3621     
3622     
3623     
3624     main : false,
3625     
3626     
3627     tag : false,
3628     
3629     
3630     getAutoCreate : function(){
3631         
3632         
3633         var cfg = {
3634             tag : this.tag || 'div',
3635             cls : 'navbar'
3636         };
3637           
3638         
3639         cfg.cn = [
3640             {
3641                 cls: 'nav',
3642                 tag : 'ul'
3643             }
3644         ];
3645         
3646          
3647         this.type = this.type || 'nav';
3648         if (['tabs','pills'].indexOf(this.type)!==-1) {
3649             cfg.cn[0].cls += ' nav-' + this.type
3650         
3651         
3652         } else {
3653             if (this.type!=='nav') {
3654                 Roo.log('nav type must be nav/tabs/pills')
3655             }
3656             cfg.cn[0].cls += ' navbar-nav'
3657         }
3658         
3659         
3660         
3661         
3662         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3663             cfg.cn[0].cls += ' nav-' + this.arrangement;
3664         }
3665         
3666         
3667         if (this.align === 'right') {
3668             cfg.cn[0].cls += ' navbar-right';
3669         }
3670         
3671         if (this.inverse) {
3672             cfg.cls += ' navbar-inverse';
3673             
3674         }
3675         
3676         
3677         return cfg;
3678     
3679         
3680     }
3681     
3682     
3683     
3684 });
3685
3686
3687
3688  
3689
3690  
3691        /*
3692  * - LGPL
3693  *
3694  * navbar
3695  * 
3696  */
3697
3698 /**
3699  * @class Roo.bootstrap.NavHeaderbar
3700  * @extends Roo.bootstrap.NavSimplebar
3701  * Bootstrap Sidebar class
3702  *
3703  * @cfg {String} brand what is brand
3704  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3705  * @cfg {String} brand_href href of the brand
3706  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3707  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3708  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3709  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3710  * 
3711  * @constructor
3712  * Create a new Sidebar
3713  * @param {Object} config The config object
3714  */
3715
3716
3717 Roo.bootstrap.NavHeaderbar = function(config){
3718     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3719       
3720 };
3721
3722 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3723     
3724     position: '',
3725     brand: '',
3726     brand_href: false,
3727     srButton : true,
3728     autohide : false,
3729     desktopCenter : false,
3730    
3731     
3732     getAutoCreate : function(){
3733         
3734         var   cfg = {
3735             tag: this.nav || 'nav',
3736             cls: 'navbar',
3737             role: 'navigation',
3738             cn: []
3739         };
3740         
3741         var cn = cfg.cn;
3742         if (this.desktopCenter) {
3743             cn.push({cls : 'container', cn : []});
3744             cn = cn[0].cn;
3745         }
3746         
3747         if(this.srButton){
3748             cn.push({
3749                 tag: 'div',
3750                 cls: 'navbar-header',
3751                 cn: [
3752                     {
3753                         tag: 'button',
3754                         type: 'button',
3755                         cls: 'navbar-toggle',
3756                         'data-toggle': 'collapse',
3757                         cn: [
3758                             {
3759                                 tag: 'span',
3760                                 cls: 'sr-only',
3761                                 html: 'Toggle navigation'
3762                             },
3763                             {
3764                                 tag: 'span',
3765                                 cls: 'icon-bar'
3766                             },
3767                             {
3768                                 tag: 'span',
3769                                 cls: 'icon-bar'
3770                             },
3771                             {
3772                                 tag: 'span',
3773                                 cls: 'icon-bar'
3774                             }
3775                         ]
3776                     }
3777                 ]
3778             });
3779         }
3780         
3781         cn.push({
3782             tag: 'div',
3783             cls: 'collapse navbar-collapse',
3784             cn : []
3785         });
3786         
3787         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3788         
3789         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3790             cfg.cls += ' navbar-' + this.position;
3791             
3792             // tag can override this..
3793             
3794             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3795         }
3796         
3797         if (this.brand !== '') {
3798             cn[0].cn.push({
3799                 tag: 'a',
3800                 href: this.brand_href ? this.brand_href : '#',
3801                 cls: 'navbar-brand',
3802                 cn: [
3803                 this.brand
3804                 ]
3805             });
3806         }
3807         
3808         if(this.main){
3809             cfg.cls += ' main-nav';
3810         }
3811         
3812         
3813         return cfg;
3814
3815         
3816     },
3817     getHeaderChildContainer : function()
3818     {
3819         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3820             return this.el.select('.navbar-header',true).first();
3821         }
3822         
3823         return this.getChildContainer();
3824     },
3825     
3826     
3827     initEvents : function()
3828     {
3829         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3830         
3831         if (this.autohide) {
3832             
3833             var prevScroll = 0;
3834             var ft = this.el;
3835             
3836             Roo.get(document).on('scroll',function(e) {
3837                 var ns = Roo.get(document).getScroll().top;
3838                 var os = prevScroll;
3839                 prevScroll = ns;
3840                 
3841                 if(ns > os){
3842                     ft.removeClass('slideDown');
3843                     ft.addClass('slideUp');
3844                     return;
3845                 }
3846                 ft.removeClass('slideUp');
3847                 ft.addClass('slideDown');
3848                  
3849               
3850           },this);
3851         }
3852     }    
3853     
3854 });
3855
3856
3857
3858  
3859
3860  /*
3861  * - LGPL
3862  *
3863  * navbar
3864  * 
3865  */
3866
3867 /**
3868  * @class Roo.bootstrap.NavSidebar
3869  * @extends Roo.bootstrap.Navbar
3870  * Bootstrap Sidebar class
3871  * 
3872  * @constructor
3873  * Create a new Sidebar
3874  * @param {Object} config The config object
3875  */
3876
3877
3878 Roo.bootstrap.NavSidebar = function(config){
3879     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3880 };
3881
3882 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3883     
3884     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3885     
3886     getAutoCreate : function(){
3887         
3888         
3889         return  {
3890             tag: 'div',
3891             cls: 'sidebar sidebar-nav'
3892         };
3893     
3894         
3895     }
3896     
3897     
3898     
3899 });
3900
3901
3902
3903  
3904
3905  /*
3906  * - LGPL
3907  *
3908  * nav group
3909  * 
3910  */
3911
3912 /**
3913  * @class Roo.bootstrap.NavGroup
3914  * @extends Roo.bootstrap.Component
3915  * Bootstrap NavGroup class
3916  * @cfg {String} align (left|right)
3917  * @cfg {Boolean} inverse
3918  * @cfg {String} type (nav|pills|tab) default nav
3919  * @cfg {String} navId - reference Id for navbar.
3920
3921  * 
3922  * @constructor
3923  * Create a new nav group
3924  * @param {Object} config The config object
3925  */
3926
3927 Roo.bootstrap.NavGroup = function(config){
3928     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3929     this.navItems = [];
3930    
3931     Roo.bootstrap.NavGroup.register(this);
3932      this.addEvents({
3933         /**
3934              * @event changed
3935              * Fires when the active item changes
3936              * @param {Roo.bootstrap.NavGroup} this
3937              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3938              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3939          */
3940         'changed': true
3941      });
3942     
3943 };
3944
3945 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3946     
3947     align: '',
3948     inverse: false,
3949     form: false,
3950     type: 'nav',
3951     navId : '',
3952     // private
3953     
3954     navItems : false, 
3955     
3956     getAutoCreate : function()
3957     {
3958         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3959         
3960         cfg = {
3961             tag : 'ul',
3962             cls: 'nav' 
3963         };
3964         
3965         if (['tabs','pills'].indexOf(this.type)!==-1) {
3966             cfg.cls += ' nav-' + this.type
3967         } else {
3968             if (this.type!=='nav') {
3969                 Roo.log('nav type must be nav/tabs/pills')
3970             }
3971             cfg.cls += ' navbar-nav'
3972         }
3973         
3974         if (this.parent().sidebar) {
3975             cfg = {
3976                 tag: 'ul',
3977                 cls: 'dashboard-menu sidebar-menu'
3978             };
3979             
3980             return cfg;
3981         }
3982         
3983         if (this.form === true) {
3984             cfg = {
3985                 tag: 'form',
3986                 cls: 'navbar-form'
3987             };
3988             
3989             if (this.align === 'right') {
3990                 cfg.cls += ' navbar-right';
3991             } else {
3992                 cfg.cls += ' navbar-left';
3993             }
3994         }
3995         
3996         if (this.align === 'right') {
3997             cfg.cls += ' navbar-right';
3998         }
3999         
4000         if (this.inverse) {
4001             cfg.cls += ' navbar-inverse';
4002             
4003         }
4004         
4005         
4006         return cfg;
4007     },
4008     /**
4009     * sets the active Navigation item
4010     * @param {Roo.bootstrap.NavItem} the new current navitem
4011     */
4012     setActiveItem : function(item)
4013     {
4014         var prev = false;
4015         Roo.each(this.navItems, function(v){
4016             if (v == item) {
4017                 return ;
4018             }
4019             if (v.isActive()) {
4020                 v.setActive(false, true);
4021                 prev = v;
4022                 
4023             }
4024             
4025         });
4026
4027         item.setActive(true, true);
4028         this.fireEvent('changed', this, item, prev);
4029         
4030         
4031     },
4032     /**
4033     * gets the active Navigation item
4034     * @return {Roo.bootstrap.NavItem} the current navitem
4035     */
4036     getActive : function()
4037     {
4038         
4039         var prev = false;
4040         Roo.each(this.navItems, function(v){
4041             
4042             if (v.isActive()) {
4043                 prev = v;
4044                 
4045             }
4046             
4047         });
4048         return prev;
4049     },
4050     
4051     indexOfNav : function()
4052     {
4053         
4054         var prev = false;
4055         Roo.each(this.navItems, function(v,i){
4056             
4057             if (v.isActive()) {
4058                 prev = i;
4059                 
4060             }
4061             
4062         });
4063         return prev;
4064     },
4065     /**
4066     * adds a Navigation item
4067     * @param {Roo.bootstrap.NavItem} the navitem to add
4068     */
4069     addItem : function(cfg)
4070     {
4071         var cn = new Roo.bootstrap.NavItem(cfg);
4072         this.register(cn);
4073         cn.parentId = this.id;
4074         cn.onRender(this.el, null);
4075         return cn;
4076     },
4077     /**
4078     * register a Navigation item
4079     * @param {Roo.bootstrap.NavItem} the navitem to add
4080     */
4081     register : function(item)
4082     {
4083         this.navItems.push( item);
4084         item.navId = this.navId;
4085     
4086     },
4087     
4088     /**
4089     * clear all the Navigation item
4090     */
4091    
4092     clearAll : function()
4093     {
4094         this.navItems = [];
4095         this.el.dom.innerHTML = '';
4096     },
4097     
4098     getNavItem: function(tabId)
4099     {
4100         var ret = false;
4101         Roo.each(this.navItems, function(e) {
4102             if (e.tabId == tabId) {
4103                ret =  e;
4104                return false;
4105             }
4106             return true;
4107             
4108         });
4109         return ret;
4110     },
4111     
4112     setActiveNext : function()
4113     {
4114         var i = this.indexOfNav(this.getActive());
4115         if (i > this.navItems.length) {
4116             return;
4117         }
4118         this.setActiveItem(this.navItems[i+1]);
4119     },
4120     setActivePrev : function()
4121     {
4122         var i = this.indexOfNav(this.getActive());
4123         if (i  < 1) {
4124             return;
4125         }
4126         this.setActiveItem(this.navItems[i-1]);
4127     },
4128     clearWasActive : function(except) {
4129         Roo.each(this.navItems, function(e) {
4130             if (e.tabId != except.tabId && e.was_active) {
4131                e.was_active = false;
4132                return false;
4133             }
4134             return true;
4135             
4136         });
4137     },
4138     getWasActive : function ()
4139     {
4140         var r = false;
4141         Roo.each(this.navItems, function(e) {
4142             if (e.was_active) {
4143                r = e;
4144                return false;
4145             }
4146             return true;
4147             
4148         });
4149         return r;
4150     }
4151     
4152     
4153 });
4154
4155  
4156 Roo.apply(Roo.bootstrap.NavGroup, {
4157     
4158     groups: {},
4159      /**
4160     * register a Navigation Group
4161     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4162     */
4163     register : function(navgrp)
4164     {
4165         this.groups[navgrp.navId] = navgrp;
4166         
4167     },
4168     /**
4169     * fetch a Navigation Group based on the navigation ID
4170     * @param {string} the navgroup to add
4171     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4172     */
4173     get: function(navId) {
4174         if (typeof(this.groups[navId]) == 'undefined') {
4175             return false;
4176             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4177         }
4178         return this.groups[navId] ;
4179     }
4180     
4181     
4182     
4183 });
4184
4185  /*
4186  * - LGPL
4187  *
4188  * row
4189  * 
4190  */
4191
4192 /**
4193  * @class Roo.bootstrap.NavItem
4194  * @extends Roo.bootstrap.Component
4195  * Bootstrap Navbar.NavItem class
4196  * @cfg {String} href  link to
4197  * @cfg {String} html content of button
4198  * @cfg {String} badge text inside badge
4199  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4200  * @cfg {String} glyphicon name of glyphicon
4201  * @cfg {String} icon name of font awesome icon
4202  * @cfg {Boolean} active Is item active
4203  * @cfg {Boolean} disabled Is item disabled
4204  
4205  * @cfg {Boolean} preventDefault (true | false) default false
4206  * @cfg {String} tabId the tab that this item activates.
4207  * @cfg {String} tagtype (a|span) render as a href or span?
4208  * @cfg {Boolean} animateRef (true|false) link to element default false  
4209   
4210  * @constructor
4211  * Create a new Navbar Item
4212  * @param {Object} config The config object
4213  */
4214 Roo.bootstrap.NavItem = function(config){
4215     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4216     this.addEvents({
4217         // raw events
4218         /**
4219          * @event click
4220          * The raw click event for the entire grid.
4221          * @param {Roo.EventObject} e
4222          */
4223         "click" : true,
4224          /**
4225             * @event changed
4226             * Fires when the active item active state changes
4227             * @param {Roo.bootstrap.NavItem} this
4228             * @param {boolean} state the new state
4229              
4230          */
4231         'changed': true,
4232         /**
4233             * @event scrollto
4234             * Fires when scroll to element
4235             * @param {Roo.bootstrap.NavItem} this
4236             * @param {Object} options
4237             * @param {Roo.EventObject} e
4238              
4239          */
4240         'scrollto': true
4241     });
4242    
4243 };
4244
4245 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4246     
4247     href: false,
4248     html: '',
4249     badge: '',
4250     icon: false,
4251     glyphicon: false,
4252     active: false,
4253     preventDefault : false,
4254     tabId : false,
4255     tagtype : 'a',
4256     disabled : false,
4257     animateRef : false,
4258     was_active : false,
4259     
4260     getAutoCreate : function(){
4261          
4262         var cfg = {
4263             tag: 'li',
4264             cls: 'nav-item'
4265             
4266         };
4267         
4268         if (this.active) {
4269             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4270         }
4271         if (this.disabled) {
4272             cfg.cls += ' disabled';
4273         }
4274         
4275         if (this.href || this.html || this.glyphicon || this.icon) {
4276             cfg.cn = [
4277                 {
4278                     tag: this.tagtype,
4279                     href : this.href || "#",
4280                     html: this.html || ''
4281                 }
4282             ];
4283             
4284             if (this.icon) {
4285                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4286             }
4287
4288             if(this.glyphicon) {
4289                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4290             }
4291             
4292             if (this.menu) {
4293                 
4294                 cfg.cn[0].html += " <span class='caret'></span>";
4295              
4296             }
4297             
4298             if (this.badge !== '') {
4299                  
4300                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4301             }
4302         }
4303         
4304         
4305         
4306         return cfg;
4307     },
4308     initEvents: function() 
4309     {
4310         if (typeof (this.menu) != 'undefined') {
4311             this.menu.parentType = this.xtype;
4312             this.menu.triggerEl = this.el;
4313             this.menu = this.addxtype(Roo.apply({}, this.menu));
4314         }
4315         
4316         this.el.select('a',true).on('click', this.onClick, this);
4317         
4318         if(this.tagtype == 'span'){
4319             this.el.select('span',true).on('click', this.onClick, this);
4320         }
4321        
4322         // at this point parent should be available..
4323         this.parent().register(this);
4324     },
4325     
4326     onClick : function(e)
4327     {
4328         if (e.getTarget('.dropdown-menu-item')) {
4329             // did you click on a menu itemm.... - then don't trigger onclick..
4330             return;
4331         }
4332         
4333         if(
4334                 this.preventDefault || 
4335                 this.href == '#' 
4336         ){
4337             Roo.log("NavItem - prevent Default?");
4338             e.preventDefault();
4339         }
4340         
4341         if (this.disabled) {
4342             return;
4343         }
4344         
4345         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4346         if (tg && tg.transition) {
4347             Roo.log("waiting for the transitionend");
4348             return;
4349         }
4350         
4351         
4352         
4353         //Roo.log("fire event clicked");
4354         if(this.fireEvent('click', this, e) === false){
4355             return;
4356         };
4357         
4358         if(this.tagtype == 'span'){
4359             return;
4360         }
4361         
4362         //Roo.log(this.href);
4363         var ael = this.el.select('a',true).first();
4364         //Roo.log(ael);
4365         
4366         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4367             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4368             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4369                 return; // ignore... - it's a 'hash' to another page.
4370             }
4371             Roo.log("NavItem - prevent Default?");
4372             e.preventDefault();
4373             this.scrollToElement(e);
4374         }
4375         
4376         
4377         var p =  this.parent();
4378    
4379         if (['tabs','pills'].indexOf(p.type)!==-1) {
4380             if (typeof(p.setActiveItem) !== 'undefined') {
4381                 p.setActiveItem(this);
4382             }
4383         }
4384         
4385         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4386         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4387             // remove the collapsed menu expand...
4388             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4389         }
4390     },
4391     
4392     isActive: function () {
4393         return this.active
4394     },
4395     setActive : function(state, fire, is_was_active)
4396     {
4397         if (this.active && !state && this.navId) {
4398             this.was_active = true;
4399             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4400             if (nv) {
4401                 nv.clearWasActive(this);
4402             }
4403             
4404         }
4405         this.active = state;
4406         
4407         if (!state ) {
4408             this.el.removeClass('active');
4409         } else if (!this.el.hasClass('active')) {
4410             this.el.addClass('active');
4411         }
4412         if (fire) {
4413             this.fireEvent('changed', this, state);
4414         }
4415         
4416         // show a panel if it's registered and related..
4417         
4418         if (!this.navId || !this.tabId || !state || is_was_active) {
4419             return;
4420         }
4421         
4422         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4423         if (!tg) {
4424             return;
4425         }
4426         var pan = tg.getPanelByName(this.tabId);
4427         if (!pan) {
4428             return;
4429         }
4430         // if we can not flip to new panel - go back to old nav highlight..
4431         if (false == tg.showPanel(pan)) {
4432             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4433             if (nv) {
4434                 var onav = nv.getWasActive();
4435                 if (onav) {
4436                     onav.setActive(true, false, true);
4437                 }
4438             }
4439             
4440         }
4441         
4442         
4443         
4444     },
4445      // this should not be here...
4446     setDisabled : function(state)
4447     {
4448         this.disabled = state;
4449         if (!state ) {
4450             this.el.removeClass('disabled');
4451         } else if (!this.el.hasClass('disabled')) {
4452             this.el.addClass('disabled');
4453         }
4454         
4455     },
4456     
4457     /**
4458      * Fetch the element to display the tooltip on.
4459      * @return {Roo.Element} defaults to this.el
4460      */
4461     tooltipEl : function()
4462     {
4463         return this.el.select('' + this.tagtype + '', true).first();
4464     },
4465     
4466     scrollToElement : function(e)
4467     {
4468         var c = document.body;
4469         
4470         /*
4471          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4472          */
4473         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4474             c = document.documentElement;
4475         }
4476         
4477         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4478         
4479         if(!target){
4480             return;
4481         }
4482
4483         var o = target.calcOffsetsTo(c);
4484         
4485         var options = {
4486             target : target,
4487             value : o[1]
4488         };
4489         
4490         this.fireEvent('scrollto', this, options, e);
4491         
4492         Roo.get(c).scrollTo('top', options.value, true);
4493         
4494         return;
4495     }
4496 });
4497  
4498
4499  /*
4500  * - LGPL
4501  *
4502  * sidebar item
4503  *
4504  *  li
4505  *    <span> icon </span>
4506  *    <span> text </span>
4507  *    <span>badge </span>
4508  */
4509
4510 /**
4511  * @class Roo.bootstrap.NavSidebarItem
4512  * @extends Roo.bootstrap.NavItem
4513  * Bootstrap Navbar.NavSidebarItem class
4514  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4515  * {bool} open is the menu open
4516  * @constructor
4517  * Create a new Navbar Button
4518  * @param {Object} config The config object
4519  */
4520 Roo.bootstrap.NavSidebarItem = function(config){
4521     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4522     this.addEvents({
4523         // raw events
4524         /**
4525          * @event click
4526          * The raw click event for the entire grid.
4527          * @param {Roo.EventObject} e
4528          */
4529         "click" : true,
4530          /**
4531             * @event changed
4532             * Fires when the active item active state changes
4533             * @param {Roo.bootstrap.NavSidebarItem} this
4534             * @param {boolean} state the new state
4535              
4536          */
4537         'changed': true
4538     });
4539    
4540 };
4541
4542 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4543     
4544     badgeWeight : 'default',
4545     
4546     open: false,
4547     
4548     getAutoCreate : function(){
4549         
4550         
4551         var a = {
4552                 tag: 'a',
4553                 href : this.href || '#',
4554                 cls: '',
4555                 html : '',
4556                 cn : []
4557         };
4558         var cfg = {
4559             tag: 'li',
4560             cls: '',
4561             cn: [ a ]
4562         };
4563         var span = {
4564             tag: 'span',
4565             html : this.html || ''
4566         };
4567         
4568         
4569         if (this.active) {
4570             cfg.cls += ' active';
4571         }
4572         
4573         if (this.disabled) {
4574             cfg.cls += ' disabled';
4575         }
4576         if (this.open) {
4577             cfg.cls += ' open x-open';
4578         }
4579         // left icon..
4580         if (this.glyphicon || this.icon) {
4581             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4582             a.cn.push({ tag : 'i', cls : c }) ;
4583         }
4584         // html..
4585         a.cn.push(span);
4586         // then badge..
4587         if (this.badge !== '') {
4588             
4589             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4590         }
4591         // fi
4592         if (this.menu) {
4593             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4594             a.cls += 'dropdown-toggle treeview' ;
4595         }
4596         
4597         return cfg;
4598          
4599            
4600     },
4601     
4602     initEvents : function()
4603     { 
4604         if (typeof (this.menu) != 'undefined') {
4605             this.menu.parentType = this.xtype;
4606             this.menu.triggerEl = this.el;
4607             this.menu = this.addxtype(Roo.apply({}, this.menu));
4608         }
4609         
4610         this.el.on('click', this.onClick, this);
4611        
4612     
4613         if(this.badge !== ''){
4614  
4615             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4616         }
4617         
4618     },
4619     
4620     onClick : function(e)
4621     {
4622         if(this.disabled){
4623             e.preventDefault();
4624             return;
4625         }
4626         
4627         if(this.preventDefault){
4628             e.preventDefault();
4629         }
4630         
4631         this.fireEvent('click', this);
4632     },
4633     
4634     disable : function()
4635     {
4636         this.setDisabled(true);
4637     },
4638     
4639     enable : function()
4640     {
4641         this.setDisabled(false);
4642     },
4643     
4644     setDisabled : function(state)
4645     {
4646         if(this.disabled == state){
4647             return;
4648         }
4649         
4650         this.disabled = state;
4651         
4652         if (state) {
4653             this.el.addClass('disabled');
4654             return;
4655         }
4656         
4657         this.el.removeClass('disabled');
4658         
4659         return;
4660     },
4661     
4662     setActive : function(state)
4663     {
4664         if(this.active == state){
4665             return;
4666         }
4667         
4668         this.active = state;
4669         
4670         if (state) {
4671             this.el.addClass('active');
4672             return;
4673         }
4674         
4675         this.el.removeClass('active');
4676         
4677         return;
4678     },
4679     
4680     isActive: function () 
4681     {
4682         return this.active;
4683     },
4684     
4685     setBadge : function(str)
4686     {
4687         if(!this.badgeEl){
4688             return;
4689         }
4690         
4691         this.badgeEl.dom.innerHTML = str;
4692     }
4693     
4694    
4695      
4696  
4697 });
4698  
4699
4700  /*
4701  * - LGPL
4702  *
4703  * row
4704  * 
4705  */
4706
4707 /**
4708  * @class Roo.bootstrap.Row
4709  * @extends Roo.bootstrap.Component
4710  * Bootstrap Row class (contains columns...)
4711  * 
4712  * @constructor
4713  * Create a new Row
4714  * @param {Object} config The config object
4715  */
4716
4717 Roo.bootstrap.Row = function(config){
4718     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4719 };
4720
4721 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4722     
4723     getAutoCreate : function(){
4724        return {
4725             cls: 'row clearfix'
4726        };
4727     }
4728     
4729     
4730 });
4731
4732  
4733
4734  /*
4735  * - LGPL
4736  *
4737  * element
4738  * 
4739  */
4740
4741 /**
4742  * @class Roo.bootstrap.Element
4743  * @extends Roo.bootstrap.Component
4744  * Bootstrap Element class
4745  * @cfg {String} html contents of the element
4746  * @cfg {String} tag tag of the element
4747  * @cfg {String} cls class of the element
4748  * @cfg {Boolean} preventDefault (true|false) default false
4749  * @cfg {Boolean} clickable (true|false) default false
4750  * 
4751  * @constructor
4752  * Create a new Element
4753  * @param {Object} config The config object
4754  */
4755
4756 Roo.bootstrap.Element = function(config){
4757     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4758     
4759     this.addEvents({
4760         // raw events
4761         /**
4762          * @event click
4763          * When a element is chick
4764          * @param {Roo.bootstrap.Element} this
4765          * @param {Roo.EventObject} e
4766          */
4767         "click" : true
4768     });
4769 };
4770
4771 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4772     
4773     tag: 'div',
4774     cls: '',
4775     html: '',
4776     preventDefault: false, 
4777     clickable: false,
4778     
4779     getAutoCreate : function(){
4780         
4781         var cfg = {
4782             tag: this.tag,
4783             cls: this.cls,
4784             html: this.html
4785         };
4786         
4787         return cfg;
4788     },
4789     
4790     initEvents: function() 
4791     {
4792         Roo.bootstrap.Element.superclass.initEvents.call(this);
4793         
4794         if(this.clickable){
4795             this.el.on('click', this.onClick, this);
4796         }
4797         
4798     },
4799     
4800     onClick : function(e)
4801     {
4802         if(this.preventDefault){
4803             e.preventDefault();
4804         }
4805         
4806         this.fireEvent('click', this, e);
4807     },
4808     
4809     getValue : function()
4810     {
4811         return this.el.dom.innerHTML;
4812     },
4813     
4814     setValue : function(value)
4815     {
4816         this.el.dom.innerHTML = value;
4817     }
4818    
4819 });
4820
4821  
4822
4823  /*
4824  * - LGPL
4825  *
4826  * pagination
4827  * 
4828  */
4829
4830 /**
4831  * @class Roo.bootstrap.Pagination
4832  * @extends Roo.bootstrap.Component
4833  * Bootstrap Pagination class
4834  * @cfg {String} size xs | sm | md | lg
4835  * @cfg {Boolean} inverse false | true
4836  * 
4837  * @constructor
4838  * Create a new Pagination
4839  * @param {Object} config The config object
4840  */
4841
4842 Roo.bootstrap.Pagination = function(config){
4843     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4844 };
4845
4846 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4847     
4848     cls: false,
4849     size: false,
4850     inverse: false,
4851     
4852     getAutoCreate : function(){
4853         var cfg = {
4854             tag: 'ul',
4855                 cls: 'pagination'
4856         };
4857         if (this.inverse) {
4858             cfg.cls += ' inverse';
4859         }
4860         if (this.html) {
4861             cfg.html=this.html;
4862         }
4863         if (this.cls) {
4864             cfg.cls += " " + this.cls;
4865         }
4866         return cfg;
4867     }
4868    
4869 });
4870
4871  
4872
4873  /*
4874  * - LGPL
4875  *
4876  * Pagination item
4877  * 
4878  */
4879
4880
4881 /**
4882  * @class Roo.bootstrap.PaginationItem
4883  * @extends Roo.bootstrap.Component
4884  * Bootstrap PaginationItem class
4885  * @cfg {String} html text
4886  * @cfg {String} href the link
4887  * @cfg {Boolean} preventDefault (true | false) default true
4888  * @cfg {Boolean} active (true | false) default false
4889  * @cfg {Boolean} disabled default false
4890  * 
4891  * 
4892  * @constructor
4893  * Create a new PaginationItem
4894  * @param {Object} config The config object
4895  */
4896
4897
4898 Roo.bootstrap.PaginationItem = function(config){
4899     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4900     this.addEvents({
4901         // raw events
4902         /**
4903          * @event click
4904          * The raw click event for the entire grid.
4905          * @param {Roo.EventObject} e
4906          */
4907         "click" : true
4908     });
4909 };
4910
4911 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4912     
4913     href : false,
4914     html : false,
4915     preventDefault: true,
4916     active : false,
4917     cls : false,
4918     disabled: false,
4919     
4920     getAutoCreate : function(){
4921         var cfg= {
4922             tag: 'li',
4923             cn: [
4924                 {
4925                     tag : 'a',
4926                     href : this.href ? this.href : '#',
4927                     html : this.html ? this.html : ''
4928                 }
4929             ]
4930         };
4931         
4932         if(this.cls){
4933             cfg.cls = this.cls;
4934         }
4935         
4936         if(this.disabled){
4937             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4938         }
4939         
4940         if(this.active){
4941             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4942         }
4943         
4944         return cfg;
4945     },
4946     
4947     initEvents: function() {
4948         
4949         this.el.on('click', this.onClick, this);
4950         
4951     },
4952     onClick : function(e)
4953     {
4954         Roo.log('PaginationItem on click ');
4955         if(this.preventDefault){
4956             e.preventDefault();
4957         }
4958         
4959         if(this.disabled){
4960             return;
4961         }
4962         
4963         this.fireEvent('click', this, e);
4964     }
4965    
4966 });
4967
4968  
4969
4970  /*
4971  * - LGPL
4972  *
4973  * slider
4974  * 
4975  */
4976
4977
4978 /**
4979  * @class Roo.bootstrap.Slider
4980  * @extends Roo.bootstrap.Component
4981  * Bootstrap Slider class
4982  *    
4983  * @constructor
4984  * Create a new Slider
4985  * @param {Object} config The config object
4986  */
4987
4988 Roo.bootstrap.Slider = function(config){
4989     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4990 };
4991
4992 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4993     
4994     getAutoCreate : function(){
4995         
4996         var cfg = {
4997             tag: 'div',
4998             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4999             cn: [
5000                 {
5001                     tag: 'a',
5002                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5003                 }
5004             ]
5005         };
5006         
5007         return cfg;
5008     }
5009    
5010 });
5011
5012  /*
5013  * Based on:
5014  * Ext JS Library 1.1.1
5015  * Copyright(c) 2006-2007, Ext JS, LLC.
5016  *
5017  * Originally Released Under LGPL - original licence link has changed is not relivant.
5018  *
5019  * Fork - LGPL
5020  * <script type="text/javascript">
5021  */
5022  
5023
5024 /**
5025  * @class Roo.grid.ColumnModel
5026  * @extends Roo.util.Observable
5027  * This is the default implementation of a ColumnModel used by the Grid. It defines
5028  * the columns in the grid.
5029  * <br>Usage:<br>
5030  <pre><code>
5031  var colModel = new Roo.grid.ColumnModel([
5032         {header: "Ticker", width: 60, sortable: true, locked: true},
5033         {header: "Company Name", width: 150, sortable: true},
5034         {header: "Market Cap.", width: 100, sortable: true},
5035         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5036         {header: "Employees", width: 100, sortable: true, resizable: false}
5037  ]);
5038  </code></pre>
5039  * <p>
5040  
5041  * The config options listed for this class are options which may appear in each
5042  * individual column definition.
5043  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5044  * @constructor
5045  * @param {Object} config An Array of column config objects. See this class's
5046  * config objects for details.
5047 */
5048 Roo.grid.ColumnModel = function(config){
5049         /**
5050      * The config passed into the constructor
5051      */
5052     this.config = config;
5053     this.lookup = {};
5054
5055     // if no id, create one
5056     // if the column does not have a dataIndex mapping,
5057     // map it to the order it is in the config
5058     for(var i = 0, len = config.length; i < len; i++){
5059         var c = config[i];
5060         if(typeof c.dataIndex == "undefined"){
5061             c.dataIndex = i;
5062         }
5063         if(typeof c.renderer == "string"){
5064             c.renderer = Roo.util.Format[c.renderer];
5065         }
5066         if(typeof c.id == "undefined"){
5067             c.id = Roo.id();
5068         }
5069         if(c.editor && c.editor.xtype){
5070             c.editor  = Roo.factory(c.editor, Roo.grid);
5071         }
5072         if(c.editor && c.editor.isFormField){
5073             c.editor = new Roo.grid.GridEditor(c.editor);
5074         }
5075         this.lookup[c.id] = c;
5076     }
5077
5078     /**
5079      * The width of columns which have no width specified (defaults to 100)
5080      * @type Number
5081      */
5082     this.defaultWidth = 100;
5083
5084     /**
5085      * Default sortable of columns which have no sortable specified (defaults to false)
5086      * @type Boolean
5087      */
5088     this.defaultSortable = false;
5089
5090     this.addEvents({
5091         /**
5092              * @event widthchange
5093              * Fires when the width of a column changes.
5094              * @param {ColumnModel} this
5095              * @param {Number} columnIndex The column index
5096              * @param {Number} newWidth The new width
5097              */
5098             "widthchange": true,
5099         /**
5100              * @event headerchange
5101              * Fires when the text of a header changes.
5102              * @param {ColumnModel} this
5103              * @param {Number} columnIndex The column index
5104              * @param {Number} newText The new header text
5105              */
5106             "headerchange": true,
5107         /**
5108              * @event hiddenchange
5109              * Fires when a column is hidden or "unhidden".
5110              * @param {ColumnModel} this
5111              * @param {Number} columnIndex The column index
5112              * @param {Boolean} hidden true if hidden, false otherwise
5113              */
5114             "hiddenchange": true,
5115             /**
5116          * @event columnmoved
5117          * Fires when a column is moved.
5118          * @param {ColumnModel} this
5119          * @param {Number} oldIndex
5120          * @param {Number} newIndex
5121          */
5122         "columnmoved" : true,
5123         /**
5124          * @event columlockchange
5125          * Fires when a column's locked state is changed
5126          * @param {ColumnModel} this
5127          * @param {Number} colIndex
5128          * @param {Boolean} locked true if locked
5129          */
5130         "columnlockchange" : true
5131     });
5132     Roo.grid.ColumnModel.superclass.constructor.call(this);
5133 };
5134 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5135     /**
5136      * @cfg {String} header The header text to display in the Grid view.
5137      */
5138     /**
5139      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5140      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5141      * specified, the column's index is used as an index into the Record's data Array.
5142      */
5143     /**
5144      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5145      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5146      */
5147     /**
5148      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5149      * Defaults to the value of the {@link #defaultSortable} property.
5150      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5151      */
5152     /**
5153      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5154      */
5155     /**
5156      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5157      */
5158     /**
5159      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5160      */
5161     /**
5162      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5163      */
5164     /**
5165      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5166      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5167      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5168      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5169      */
5170        /**
5171      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5172      */
5173     /**
5174      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5175      */
5176     /**
5177      * @cfg {String} cursor (Optional)
5178      */
5179     /**
5180      * @cfg {String} tooltip (Optional)
5181      */
5182     /**
5183      * @cfg {Number} xs (Optional)
5184      */
5185     /**
5186      * @cfg {Number} sm (Optional)
5187      */
5188     /**
5189      * @cfg {Number} md (Optional)
5190      */
5191     /**
5192      * @cfg {Number} lg (Optional)
5193      */
5194     /**
5195      * Returns the id of the column at the specified index.
5196      * @param {Number} index The column index
5197      * @return {String} the id
5198      */
5199     getColumnId : function(index){
5200         return this.config[index].id;
5201     },
5202
5203     /**
5204      * Returns the column for a specified id.
5205      * @param {String} id The column id
5206      * @return {Object} the column
5207      */
5208     getColumnById : function(id){
5209         return this.lookup[id];
5210     },
5211
5212     
5213     /**
5214      * Returns the column for a specified dataIndex.
5215      * @param {String} dataIndex The column dataIndex
5216      * @return {Object|Boolean} the column or false if not found
5217      */
5218     getColumnByDataIndex: function(dataIndex){
5219         var index = this.findColumnIndex(dataIndex);
5220         return index > -1 ? this.config[index] : false;
5221     },
5222     
5223     /**
5224      * Returns the index for a specified column id.
5225      * @param {String} id The column id
5226      * @return {Number} the index, or -1 if not found
5227      */
5228     getIndexById : function(id){
5229         for(var i = 0, len = this.config.length; i < len; i++){
5230             if(this.config[i].id == id){
5231                 return i;
5232             }
5233         }
5234         return -1;
5235     },
5236     
5237     /**
5238      * Returns the index for a specified column dataIndex.
5239      * @param {String} dataIndex The column dataIndex
5240      * @return {Number} the index, or -1 if not found
5241      */
5242     
5243     findColumnIndex : function(dataIndex){
5244         for(var i = 0, len = this.config.length; i < len; i++){
5245             if(this.config[i].dataIndex == dataIndex){
5246                 return i;
5247             }
5248         }
5249         return -1;
5250     },
5251     
5252     
5253     moveColumn : function(oldIndex, newIndex){
5254         var c = this.config[oldIndex];
5255         this.config.splice(oldIndex, 1);
5256         this.config.splice(newIndex, 0, c);
5257         this.dataMap = null;
5258         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5259     },
5260
5261     isLocked : function(colIndex){
5262         return this.config[colIndex].locked === true;
5263     },
5264
5265     setLocked : function(colIndex, value, suppressEvent){
5266         if(this.isLocked(colIndex) == value){
5267             return;
5268         }
5269         this.config[colIndex].locked = value;
5270         if(!suppressEvent){
5271             this.fireEvent("columnlockchange", this, colIndex, value);
5272         }
5273     },
5274
5275     getTotalLockedWidth : function(){
5276         var totalWidth = 0;
5277         for(var i = 0; i < this.config.length; i++){
5278             if(this.isLocked(i) && !this.isHidden(i)){
5279                 this.totalWidth += this.getColumnWidth(i);
5280             }
5281         }
5282         return totalWidth;
5283     },
5284
5285     getLockedCount : function(){
5286         for(var i = 0, len = this.config.length; i < len; i++){
5287             if(!this.isLocked(i)){
5288                 return i;
5289             }
5290         }
5291         
5292         return this.config.length;
5293     },
5294
5295     /**
5296      * Returns the number of columns.
5297      * @return {Number}
5298      */
5299     getColumnCount : function(visibleOnly){
5300         if(visibleOnly === true){
5301             var c = 0;
5302             for(var i = 0, len = this.config.length; i < len; i++){
5303                 if(!this.isHidden(i)){
5304                     c++;
5305                 }
5306             }
5307             return c;
5308         }
5309         return this.config.length;
5310     },
5311
5312     /**
5313      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5314      * @param {Function} fn
5315      * @param {Object} scope (optional)
5316      * @return {Array} result
5317      */
5318     getColumnsBy : function(fn, scope){
5319         var r = [];
5320         for(var i = 0, len = this.config.length; i < len; i++){
5321             var c = this.config[i];
5322             if(fn.call(scope||this, c, i) === true){
5323                 r[r.length] = c;
5324             }
5325         }
5326         return r;
5327     },
5328
5329     /**
5330      * Returns true if the specified column is sortable.
5331      * @param {Number} col The column index
5332      * @return {Boolean}
5333      */
5334     isSortable : function(col){
5335         if(typeof this.config[col].sortable == "undefined"){
5336             return this.defaultSortable;
5337         }
5338         return this.config[col].sortable;
5339     },
5340
5341     /**
5342      * Returns the rendering (formatting) function defined for the column.
5343      * @param {Number} col The column index.
5344      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5345      */
5346     getRenderer : function(col){
5347         if(!this.config[col].renderer){
5348             return Roo.grid.ColumnModel.defaultRenderer;
5349         }
5350         return this.config[col].renderer;
5351     },
5352
5353     /**
5354      * Sets the rendering (formatting) function for a column.
5355      * @param {Number} col The column index
5356      * @param {Function} fn The function to use to process the cell's raw data
5357      * to return HTML markup for the grid view. The render function is called with
5358      * the following parameters:<ul>
5359      * <li>Data value.</li>
5360      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5361      * <li>css A CSS style string to apply to the table cell.</li>
5362      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5363      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5364      * <li>Row index</li>
5365      * <li>Column index</li>
5366      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5367      */
5368     setRenderer : function(col, fn){
5369         this.config[col].renderer = fn;
5370     },
5371
5372     /**
5373      * Returns the width for the specified column.
5374      * @param {Number} col The column index
5375      * @return {Number}
5376      */
5377     getColumnWidth : function(col){
5378         return this.config[col].width * 1 || this.defaultWidth;
5379     },
5380
5381     /**
5382      * Sets the width for a column.
5383      * @param {Number} col The column index
5384      * @param {Number} width The new width
5385      */
5386     setColumnWidth : function(col, width, suppressEvent){
5387         this.config[col].width = width;
5388         this.totalWidth = null;
5389         if(!suppressEvent){
5390              this.fireEvent("widthchange", this, col, width);
5391         }
5392     },
5393
5394     /**
5395      * Returns the total width of all columns.
5396      * @param {Boolean} includeHidden True to include hidden column widths
5397      * @return {Number}
5398      */
5399     getTotalWidth : function(includeHidden){
5400         if(!this.totalWidth){
5401             this.totalWidth = 0;
5402             for(var i = 0, len = this.config.length; i < len; i++){
5403                 if(includeHidden || !this.isHidden(i)){
5404                     this.totalWidth += this.getColumnWidth(i);
5405                 }
5406             }
5407         }
5408         return this.totalWidth;
5409     },
5410
5411     /**
5412      * Returns the header for the specified column.
5413      * @param {Number} col The column index
5414      * @return {String}
5415      */
5416     getColumnHeader : function(col){
5417         return this.config[col].header;
5418     },
5419
5420     /**
5421      * Sets the header for a column.
5422      * @param {Number} col The column index
5423      * @param {String} header The new header
5424      */
5425     setColumnHeader : function(col, header){
5426         this.config[col].header = header;
5427         this.fireEvent("headerchange", this, col, header);
5428     },
5429
5430     /**
5431      * Returns the tooltip for the specified column.
5432      * @param {Number} col The column index
5433      * @return {String}
5434      */
5435     getColumnTooltip : function(col){
5436             return this.config[col].tooltip;
5437     },
5438     /**
5439      * Sets the tooltip for a column.
5440      * @param {Number} col The column index
5441      * @param {String} tooltip The new tooltip
5442      */
5443     setColumnTooltip : function(col, tooltip){
5444             this.config[col].tooltip = tooltip;
5445     },
5446
5447     /**
5448      * Returns the dataIndex for the specified column.
5449      * @param {Number} col The column index
5450      * @return {Number}
5451      */
5452     getDataIndex : function(col){
5453         return this.config[col].dataIndex;
5454     },
5455
5456     /**
5457      * Sets the dataIndex for a column.
5458      * @param {Number} col The column index
5459      * @param {Number} dataIndex The new dataIndex
5460      */
5461     setDataIndex : function(col, dataIndex){
5462         this.config[col].dataIndex = dataIndex;
5463     },
5464
5465     
5466     
5467     /**
5468      * Returns true if the cell is editable.
5469      * @param {Number} colIndex The column index
5470      * @param {Number} rowIndex The row index - this is nto actually used..?
5471      * @return {Boolean}
5472      */
5473     isCellEditable : function(colIndex, rowIndex){
5474         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5475     },
5476
5477     /**
5478      * Returns the editor defined for the cell/column.
5479      * return false or null to disable editing.
5480      * @param {Number} colIndex The column index
5481      * @param {Number} rowIndex The row index
5482      * @return {Object}
5483      */
5484     getCellEditor : function(colIndex, rowIndex){
5485         return this.config[colIndex].editor;
5486     },
5487
5488     /**
5489      * Sets if a column is editable.
5490      * @param {Number} col The column index
5491      * @param {Boolean} editable True if the column is editable
5492      */
5493     setEditable : function(col, editable){
5494         this.config[col].editable = editable;
5495     },
5496
5497
5498     /**
5499      * Returns true if the column is hidden.
5500      * @param {Number} colIndex The column index
5501      * @return {Boolean}
5502      */
5503     isHidden : function(colIndex){
5504         return this.config[colIndex].hidden;
5505     },
5506
5507
5508     /**
5509      * Returns true if the column width cannot be changed
5510      */
5511     isFixed : function(colIndex){
5512         return this.config[colIndex].fixed;
5513     },
5514
5515     /**
5516      * Returns true if the column can be resized
5517      * @return {Boolean}
5518      */
5519     isResizable : function(colIndex){
5520         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5521     },
5522     /**
5523      * Sets if a column is hidden.
5524      * @param {Number} colIndex The column index
5525      * @param {Boolean} hidden True if the column is hidden
5526      */
5527     setHidden : function(colIndex, hidden){
5528         this.config[colIndex].hidden = hidden;
5529         this.totalWidth = null;
5530         this.fireEvent("hiddenchange", this, colIndex, hidden);
5531     },
5532
5533     /**
5534      * Sets the editor for a column.
5535      * @param {Number} col The column index
5536      * @param {Object} editor The editor object
5537      */
5538     setEditor : function(col, editor){
5539         this.config[col].editor = editor;
5540     }
5541 });
5542
5543 Roo.grid.ColumnModel.defaultRenderer = function(value){
5544         if(typeof value == "string" && value.length < 1){
5545             return "&#160;";
5546         }
5547         return value;
5548 };
5549
5550 // Alias for backwards compatibility
5551 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5552 /*
5553  * Based on:
5554  * Ext JS Library 1.1.1
5555  * Copyright(c) 2006-2007, Ext JS, LLC.
5556  *
5557  * Originally Released Under LGPL - original licence link has changed is not relivant.
5558  *
5559  * Fork - LGPL
5560  * <script type="text/javascript">
5561  */
5562  
5563 /**
5564  * @class Roo.LoadMask
5565  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5566  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5567  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5568  * element's UpdateManager load indicator and will be destroyed after the initial load.
5569  * @constructor
5570  * Create a new LoadMask
5571  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5572  * @param {Object} config The config object
5573  */
5574 Roo.LoadMask = function(el, config){
5575     this.el = Roo.get(el);
5576     Roo.apply(this, config);
5577     if(this.store){
5578         this.store.on('beforeload', this.onBeforeLoad, this);
5579         this.store.on('load', this.onLoad, this);
5580         this.store.on('loadexception', this.onLoadException, this);
5581         this.removeMask = false;
5582     }else{
5583         var um = this.el.getUpdateManager();
5584         um.showLoadIndicator = false; // disable the default indicator
5585         um.on('beforeupdate', this.onBeforeLoad, this);
5586         um.on('update', this.onLoad, this);
5587         um.on('failure', this.onLoad, this);
5588         this.removeMask = true;
5589     }
5590 };
5591
5592 Roo.LoadMask.prototype = {
5593     /**
5594      * @cfg {Boolean} removeMask
5595      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5596      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5597      */
5598     /**
5599      * @cfg {String} msg
5600      * The text to display in a centered loading message box (defaults to 'Loading...')
5601      */
5602     msg : 'Loading...',
5603     /**
5604      * @cfg {String} msgCls
5605      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5606      */
5607     msgCls : 'x-mask-loading',
5608
5609     /**
5610      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5611      * @type Boolean
5612      */
5613     disabled: false,
5614
5615     /**
5616      * Disables the mask to prevent it from being displayed
5617      */
5618     disable : function(){
5619        this.disabled = true;
5620     },
5621
5622     /**
5623      * Enables the mask so that it can be displayed
5624      */
5625     enable : function(){
5626         this.disabled = false;
5627     },
5628     
5629     onLoadException : function()
5630     {
5631         Roo.log(arguments);
5632         
5633         if (typeof(arguments[3]) != 'undefined') {
5634             Roo.MessageBox.alert("Error loading",arguments[3]);
5635         } 
5636         /*
5637         try {
5638             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5639                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5640             }   
5641         } catch(e) {
5642             
5643         }
5644         */
5645     
5646         
5647         
5648         this.el.unmask(this.removeMask);
5649     },
5650     // private
5651     onLoad : function()
5652     {
5653         this.el.unmask(this.removeMask);
5654     },
5655
5656     // private
5657     onBeforeLoad : function(){
5658         if(!this.disabled){
5659             this.el.mask(this.msg, this.msgCls);
5660         }
5661     },
5662
5663     // private
5664     destroy : function(){
5665         if(this.store){
5666             this.store.un('beforeload', this.onBeforeLoad, this);
5667             this.store.un('load', this.onLoad, this);
5668             this.store.un('loadexception', this.onLoadException, this);
5669         }else{
5670             var um = this.el.getUpdateManager();
5671             um.un('beforeupdate', this.onBeforeLoad, this);
5672             um.un('update', this.onLoad, this);
5673             um.un('failure', this.onLoad, this);
5674         }
5675     }
5676 };/*
5677  * - LGPL
5678  *
5679  * table
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.Table
5685  * @extends Roo.bootstrap.Component
5686  * Bootstrap Table class
5687  * @cfg {String} cls table class
5688  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5689  * @cfg {String} bgcolor Specifies the background color for a table
5690  * @cfg {Number} border Specifies whether the table cells should have borders or not
5691  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5692  * @cfg {Number} cellspacing Specifies the space between cells
5693  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5694  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5695  * @cfg {String} sortable Specifies that the table should be sortable
5696  * @cfg {String} summary Specifies a summary of the content of a table
5697  * @cfg {Number} width Specifies the width of a table
5698  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5699  * 
5700  * @cfg {boolean} striped Should the rows be alternative striped
5701  * @cfg {boolean} bordered Add borders to the table
5702  * @cfg {boolean} hover Add hover highlighting
5703  * @cfg {boolean} condensed Format condensed
5704  * @cfg {boolean} responsive Format condensed
5705  * @cfg {Boolean} loadMask (true|false) default false
5706  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5707  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5708  * @cfg {Boolean} rowSelection (true|false) default false
5709  * @cfg {Boolean} cellSelection (true|false) default false
5710  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5711  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5712  
5713  * 
5714  * @constructor
5715  * Create a new Table
5716  * @param {Object} config The config object
5717  */
5718
5719 Roo.bootstrap.Table = function(config){
5720     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5721     
5722   
5723     
5724     // BC...
5725     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5726     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5727     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5728     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5729     
5730     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5731     if (this.sm) {
5732         this.sm.grid = this;
5733         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5734         this.sm = this.selModel;
5735         this.sm.xmodule = this.xmodule || false;
5736     }
5737     
5738     if (this.cm && typeof(this.cm.config) == 'undefined') {
5739         this.colModel = new Roo.grid.ColumnModel(this.cm);
5740         this.cm = this.colModel;
5741         this.cm.xmodule = this.xmodule || false;
5742     }
5743     if (this.store) {
5744         this.store= Roo.factory(this.store, Roo.data);
5745         this.ds = this.store;
5746         this.ds.xmodule = this.xmodule || false;
5747          
5748     }
5749     if (this.footer && this.store) {
5750         this.footer.dataSource = this.ds;
5751         this.footer = Roo.factory(this.footer);
5752     }
5753     
5754     /** @private */
5755     this.addEvents({
5756         /**
5757          * @event cellclick
5758          * Fires when a cell is clicked
5759          * @param {Roo.bootstrap.Table} this
5760          * @param {Roo.Element} el
5761          * @param {Number} rowIndex
5762          * @param {Number} columnIndex
5763          * @param {Roo.EventObject} e
5764          */
5765         "cellclick" : true,
5766         /**
5767          * @event celldblclick
5768          * Fires when a cell is double clicked
5769          * @param {Roo.bootstrap.Table} this
5770          * @param {Roo.Element} el
5771          * @param {Number} rowIndex
5772          * @param {Number} columnIndex
5773          * @param {Roo.EventObject} e
5774          */
5775         "celldblclick" : true,
5776         /**
5777          * @event rowclick
5778          * Fires when a row is clicked
5779          * @param {Roo.bootstrap.Table} this
5780          * @param {Roo.Element} el
5781          * @param {Number} rowIndex
5782          * @param {Roo.EventObject} e
5783          */
5784         "rowclick" : true,
5785         /**
5786          * @event rowdblclick
5787          * Fires when a row is double clicked
5788          * @param {Roo.bootstrap.Table} this
5789          * @param {Roo.Element} el
5790          * @param {Number} rowIndex
5791          * @param {Roo.EventObject} e
5792          */
5793         "rowdblclick" : true,
5794         /**
5795          * @event mouseover
5796          * Fires when a mouseover occur
5797          * @param {Roo.bootstrap.Table} this
5798          * @param {Roo.Element} el
5799          * @param {Number} rowIndex
5800          * @param {Number} columnIndex
5801          * @param {Roo.EventObject} e
5802          */
5803         "mouseover" : true,
5804         /**
5805          * @event mouseout
5806          * Fires when a mouseout occur
5807          * @param {Roo.bootstrap.Table} this
5808          * @param {Roo.Element} el
5809          * @param {Number} rowIndex
5810          * @param {Number} columnIndex
5811          * @param {Roo.EventObject} e
5812          */
5813         "mouseout" : true,
5814         /**
5815          * @event rowclass
5816          * Fires when a row is rendered, so you can change add a style to it.
5817          * @param {Roo.bootstrap.Table} this
5818          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5819          */
5820         'rowclass' : true,
5821           /**
5822          * @event rowsrendered
5823          * Fires when all the  rows have been rendered
5824          * @param {Roo.bootstrap.Table} this
5825          */
5826         'rowsrendered' : true,
5827         /**
5828          * @event contextmenu
5829          * The raw contextmenu event for the entire grid.
5830          * @param {Roo.EventObject} e
5831          */
5832         "contextmenu" : true,
5833         /**
5834          * @event rowcontextmenu
5835          * Fires when a row is right clicked
5836          * @param {Roo.bootstrap.Table} this
5837          * @param {Number} rowIndex
5838          * @param {Roo.EventObject} e
5839          */
5840         "rowcontextmenu" : true,
5841         /**
5842          * @event cellcontextmenu
5843          * Fires when a cell is right clicked
5844          * @param {Roo.bootstrap.Table} this
5845          * @param {Number} rowIndex
5846          * @param {Number} cellIndex
5847          * @param {Roo.EventObject} e
5848          */
5849          "cellcontextmenu" : true,
5850          /**
5851          * @event headercontextmenu
5852          * Fires when a header is right clicked
5853          * @param {Roo.bootstrap.Table} this
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "headercontextmenu" : true
5858     });
5859 };
5860
5861 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5862     
5863     cls: false,
5864     align: false,
5865     bgcolor: false,
5866     border: false,
5867     cellpadding: false,
5868     cellspacing: false,
5869     frame: false,
5870     rules: false,
5871     sortable: false,
5872     summary: false,
5873     width: false,
5874     striped : false,
5875     scrollBody : false,
5876     bordered: false,
5877     hover:  false,
5878     condensed : false,
5879     responsive : false,
5880     sm : false,
5881     cm : false,
5882     store : false,
5883     loadMask : false,
5884     footerShow : true,
5885     headerShow : true,
5886   
5887     rowSelection : false,
5888     cellSelection : false,
5889     layout : false,
5890     
5891     // Roo.Element - the tbody
5892     mainBody: false,
5893     // Roo.Element - thead element
5894     mainHead: false,
5895     
5896     container: false, // used by gridpanel...
5897     
5898     getAutoCreate : function()
5899     {
5900         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5901         
5902         cfg = {
5903             tag: 'table',
5904             cls : 'table',
5905             cn : []
5906         };
5907         if (this.scrollBody) {
5908             cfg.cls += ' table-body-fixed';
5909         }    
5910         if (this.striped) {
5911             cfg.cls += ' table-striped';
5912         }
5913         
5914         if (this.hover) {
5915             cfg.cls += ' table-hover';
5916         }
5917         if (this.bordered) {
5918             cfg.cls += ' table-bordered';
5919         }
5920         if (this.condensed) {
5921             cfg.cls += ' table-condensed';
5922         }
5923         if (this.responsive) {
5924             cfg.cls += ' table-responsive';
5925         }
5926         
5927         if (this.cls) {
5928             cfg.cls+=  ' ' +this.cls;
5929         }
5930         
5931         // this lot should be simplifed...
5932         
5933         if (this.align) {
5934             cfg.align=this.align;
5935         }
5936         if (this.bgcolor) {
5937             cfg.bgcolor=this.bgcolor;
5938         }
5939         if (this.border) {
5940             cfg.border=this.border;
5941         }
5942         if (this.cellpadding) {
5943             cfg.cellpadding=this.cellpadding;
5944         }
5945         if (this.cellspacing) {
5946             cfg.cellspacing=this.cellspacing;
5947         }
5948         if (this.frame) {
5949             cfg.frame=this.frame;
5950         }
5951         if (this.rules) {
5952             cfg.rules=this.rules;
5953         }
5954         if (this.sortable) {
5955             cfg.sortable=this.sortable;
5956         }
5957         if (this.summary) {
5958             cfg.summary=this.summary;
5959         }
5960         if (this.width) {
5961             cfg.width=this.width;
5962         }
5963         if (this.layout) {
5964             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5965         }
5966         
5967         if(this.store || this.cm){
5968             if(this.headerShow){
5969                 cfg.cn.push(this.renderHeader());
5970             }
5971             
5972             cfg.cn.push(this.renderBody());
5973             
5974             if(this.footerShow){
5975                 cfg.cn.push(this.renderFooter());
5976             }
5977             // where does this come from?
5978             //cfg.cls+=  ' TableGrid';
5979         }
5980         
5981         return { cn : [ cfg ] };
5982     },
5983     
5984     initEvents : function()
5985     {   
5986         if(!this.store || !this.cm){
5987             return;
5988         }
5989         if (this.selModel) {
5990             this.selModel.initEvents();
5991         }
5992         
5993         
5994         //Roo.log('initEvents with ds!!!!');
5995         
5996         this.mainBody = this.el.select('tbody', true).first();
5997         this.mainHead = this.el.select('thead', true).first();
5998         
5999         
6000         
6001         
6002         var _this = this;
6003         
6004         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6005             e.on('click', _this.sort, _this);
6006         });
6007         
6008         this.el.on("click", this.onClick, this);
6009         this.el.on("dblclick", this.onDblClick, this);
6010         
6011         // why is this done????? = it breaks dialogs??
6012         //this.parent().el.setStyle('position', 'relative');
6013         
6014         
6015         if (this.footer) {
6016             this.footer.parentId = this.id;
6017             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6018         } 
6019         
6020         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6021         
6022         this.store.on('load', this.onLoad, this);
6023         this.store.on('beforeload', this.onBeforeLoad, this);
6024         this.store.on('update', this.onUpdate, this);
6025         this.store.on('add', this.onAdd, this);
6026         this.store.on("clear", this.clear, this);
6027         
6028         this.el.on("contextmenu", this.onContextMenu, this);
6029         
6030         this.mainBody.on('scroll', this.onBodyScroll, this);
6031         
6032         
6033     },
6034     
6035     onContextMenu : function(e, t)
6036     {
6037         this.processEvent("contextmenu", e);
6038     },
6039     
6040     processEvent : function(name, e)
6041     {
6042         if (name != 'touchstart' ) {
6043             this.fireEvent(name, e);    
6044         }
6045         
6046         var t = e.getTarget();
6047         
6048         var cell = Roo.get(t);
6049         
6050         if(!cell){
6051             return;
6052         }
6053         
6054         if(cell.findParent('tfoot', false, true)){
6055             return;
6056         }
6057         
6058         if(cell.findParent('thead', false, true)){
6059             
6060             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6061                 cell = Roo.get(t).findParent('th', false, true);
6062                 if (!cell) {
6063                     Roo.log("failed to find th in thead?");
6064                     Roo.log(e.getTarget());
6065                     return;
6066                 }
6067             }
6068             
6069             var cellIndex = cell.dom.cellIndex;
6070             
6071             var ename = name == 'touchstart' ? 'click' : name;
6072             this.fireEvent("header" + ename, this, cellIndex, e);
6073             
6074             return;
6075         }
6076         
6077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6078             cell = Roo.get(t).findParent('td', false, true);
6079             if (!cell) {
6080                 Roo.log("failed to find th in tbody?");
6081                 Roo.log(e.getTarget());
6082                 return;
6083             }
6084         }
6085         
6086         var row = cell.findParent('tr', false, true);
6087         var cellIndex = cell.dom.cellIndex;
6088         var rowIndex = row.dom.rowIndex - 1;
6089         
6090         if(row !== false){
6091             
6092             this.fireEvent("row" + name, this, rowIndex, e);
6093             
6094             if(cell !== false){
6095             
6096                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6097             }
6098         }
6099         
6100     },
6101     
6102     onMouseover : function(e, el)
6103     {
6104         var cell = Roo.get(el);
6105         
6106         if(!cell){
6107             return;
6108         }
6109         
6110         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6111             cell = cell.findParent('td', false, true);
6112         }
6113         
6114         var row = cell.findParent('tr', false, true);
6115         var cellIndex = cell.dom.cellIndex;
6116         var rowIndex = row.dom.rowIndex - 1; // start from 0
6117         
6118         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6119         
6120     },
6121     
6122     onMouseout : function(e, el)
6123     {
6124         var cell = Roo.get(el);
6125         
6126         if(!cell){
6127             return;
6128         }
6129         
6130         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6131             cell = cell.findParent('td', false, true);
6132         }
6133         
6134         var row = cell.findParent('tr', false, true);
6135         var cellIndex = cell.dom.cellIndex;
6136         var rowIndex = row.dom.rowIndex - 1; // start from 0
6137         
6138         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6139         
6140     },
6141     
6142     onClick : function(e, el)
6143     {
6144         var cell = Roo.get(el);
6145         
6146         if(!cell || (!this.cellSelection && !this.rowSelection)){
6147             return;
6148         }
6149         
6150         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151             cell = cell.findParent('td', false, true);
6152         }
6153         
6154         if(!cell || typeof(cell) == 'undefined'){
6155             return;
6156         }
6157         
6158         var row = cell.findParent('tr', false, true);
6159         
6160         if(!row || typeof(row) == 'undefined'){
6161             return;
6162         }
6163         
6164         var cellIndex = cell.dom.cellIndex;
6165         var rowIndex = this.getRowIndex(row);
6166         
6167         // why??? - should these not be based on SelectionModel?
6168         if(this.cellSelection){
6169             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6170         }
6171         
6172         if(this.rowSelection){
6173             this.fireEvent('rowclick', this, row, rowIndex, e);
6174         }
6175         
6176         
6177     },
6178         
6179     onDblClick : function(e,el)
6180     {
6181         var cell = Roo.get(el);
6182         
6183         if(!cell || (!this.cellSelection && !this.rowSelection)){
6184             return;
6185         }
6186         
6187         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6188             cell = cell.findParent('td', false, true);
6189         }
6190         
6191         if(!cell || typeof(cell) == 'undefined'){
6192             return;
6193         }
6194         
6195         var row = cell.findParent('tr', false, true);
6196         
6197         if(!row || typeof(row) == 'undefined'){
6198             return;
6199         }
6200         
6201         var cellIndex = cell.dom.cellIndex;
6202         var rowIndex = this.getRowIndex(row);
6203         
6204         if(this.cellSelection){
6205             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6206         }
6207         
6208         if(this.rowSelection){
6209             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6210         }
6211     },
6212     
6213     sort : function(e,el)
6214     {
6215         var col = Roo.get(el);
6216         
6217         if(!col.hasClass('sortable')){
6218             return;
6219         }
6220         
6221         var sort = col.attr('sort');
6222         var dir = 'ASC';
6223         
6224         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6225             dir = 'DESC';
6226         }
6227         
6228         this.store.sortInfo = {field : sort, direction : dir};
6229         
6230         if (this.footer) {
6231             Roo.log("calling footer first");
6232             this.footer.onClick('first');
6233         } else {
6234         
6235             this.store.load({ params : { start : 0 } });
6236         }
6237     },
6238     
6239     renderHeader : function()
6240     {
6241         var header = {
6242             tag: 'thead',
6243             cn : []
6244         };
6245         
6246         var cm = this.cm;
6247         this.totalWidth = 0;
6248         
6249         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6250             
6251             var config = cm.config[i];
6252             
6253             var c = {
6254                 tag: 'th',
6255                 style : '',
6256                 html: cm.getColumnHeader(i)
6257             };
6258             
6259             var hh = '';
6260             
6261             if(typeof(config.sortable) != 'undefined' && config.sortable){
6262                 c.cls = 'sortable';
6263                 c.html = '<i class="glyphicon"></i>' + c.html;
6264             }
6265             
6266             if(typeof(config.lgHeader) != 'undefined'){
6267                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6268             }
6269             
6270             if(typeof(config.mdHeader) != 'undefined'){
6271                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6272             }
6273             
6274             if(typeof(config.smHeader) != 'undefined'){
6275                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6276             }
6277             
6278             if(typeof(config.xsHeader) != 'undefined'){
6279                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6280             }
6281             
6282             if(hh.length){
6283                 c.html = hh;
6284             }
6285             
6286             if(typeof(config.tooltip) != 'undefined'){
6287                 c.tooltip = config.tooltip;
6288             }
6289             
6290             if(typeof(config.colspan) != 'undefined'){
6291                 c.colspan = config.colspan;
6292             }
6293             
6294             if(typeof(config.hidden) != 'undefined' && config.hidden){
6295                 c.style += ' display:none;';
6296             }
6297             
6298             if(typeof(config.dataIndex) != 'undefined'){
6299                 c.sort = config.dataIndex;
6300             }
6301             
6302            
6303             
6304             if(typeof(config.align) != 'undefined' && config.align.length){
6305                 c.style += ' text-align:' + config.align + ';';
6306             }
6307             
6308             if(typeof(config.width) != 'undefined'){
6309                 c.style += ' width:' + config.width + 'px;';
6310                 this.totalWidth += config.width;
6311             } else {
6312                 this.totalWidth += 100; // assume minimum of 100 per column?
6313             }
6314             
6315             if(typeof(config.cls) != 'undefined'){
6316                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6317             }
6318             
6319             ['xs','sm','md','lg'].map(function(size){
6320                 
6321                 if(typeof(config[size]) == 'undefined'){
6322                     return;
6323                 }
6324                 
6325                 if (!config[size]) { // 0 = hidden
6326                     c.cls += ' hidden-' + size;
6327                     return;
6328                 }
6329                 
6330                 c.cls += ' col-' + size + '-' + config[size];
6331
6332             });
6333             
6334             header.cn.push(c)
6335         }
6336         
6337         return header;
6338     },
6339     
6340     renderBody : function()
6341     {
6342         var body = {
6343             tag: 'tbody',
6344             cn : [
6345                 {
6346                     tag: 'tr',
6347                     cn : [
6348                         {
6349                             tag : 'td',
6350                             colspan :  this.cm.getColumnCount()
6351                         }
6352                     ]
6353                 }
6354             ]
6355         };
6356         
6357         return body;
6358     },
6359     
6360     renderFooter : function()
6361     {
6362         var footer = {
6363             tag: 'tfoot',
6364             cn : [
6365                 {
6366                     tag: 'tr',
6367                     cn : [
6368                         {
6369                             tag : 'td',
6370                             colspan :  this.cm.getColumnCount()
6371                         }
6372                     ]
6373                 }
6374             ]
6375         };
6376         
6377         return footer;
6378     },
6379     
6380     
6381     
6382     onLoad : function()
6383     {
6384 //        Roo.log('ds onload');
6385         this.clear();
6386         
6387         var _this = this;
6388         var cm = this.cm;
6389         var ds = this.store;
6390         
6391         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6392             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6393             if (_this.store.sortInfo) {
6394                     
6395                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6396                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6397                 }
6398                 
6399                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6400                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6401                 }
6402             }
6403         });
6404         
6405         var tbody =  this.mainBody;
6406               
6407         if(ds.getCount() > 0){
6408             ds.data.each(function(d,rowIndex){
6409                 var row =  this.renderRow(cm, ds, rowIndex);
6410                 
6411                 tbody.createChild(row);
6412                 
6413                 var _this = this;
6414                 
6415                 if(row.cellObjects.length){
6416                     Roo.each(row.cellObjects, function(r){
6417                         _this.renderCellObject(r);
6418                     })
6419                 }
6420                 
6421             }, this);
6422         }
6423         
6424         Roo.each(this.el.select('tbody td', true).elements, function(e){
6425             e.on('mouseover', _this.onMouseover, _this);
6426         });
6427         
6428         Roo.each(this.el.select('tbody td', true).elements, function(e){
6429             e.on('mouseout', _this.onMouseout, _this);
6430         });
6431         this.fireEvent('rowsrendered', this);
6432         //if(this.loadMask){
6433         //    this.maskEl.hide();
6434         //}
6435         
6436         this.autoSize();
6437     },
6438     
6439     
6440     onUpdate : function(ds,record)
6441     {
6442         this.refreshRow(record);
6443         this.autoSize();
6444     },
6445     
6446     onRemove : function(ds, record, index, isUpdate){
6447         if(isUpdate !== true){
6448             this.fireEvent("beforerowremoved", this, index, record);
6449         }
6450         var bt = this.mainBody.dom;
6451         
6452         var rows = this.el.select('tbody > tr', true).elements;
6453         
6454         if(typeof(rows[index]) != 'undefined'){
6455             bt.removeChild(rows[index].dom);
6456         }
6457         
6458 //        if(bt.rows[index]){
6459 //            bt.removeChild(bt.rows[index]);
6460 //        }
6461         
6462         if(isUpdate !== true){
6463             //this.stripeRows(index);
6464             //this.syncRowHeights(index, index);
6465             //this.layout();
6466             this.fireEvent("rowremoved", this, index, record);
6467         }
6468     },
6469     
6470     onAdd : function(ds, records, rowIndex)
6471     {
6472         //Roo.log('on Add called');
6473         // - note this does not handle multiple adding very well..
6474         var bt = this.mainBody.dom;
6475         for (var i =0 ; i < records.length;i++) {
6476             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6477             //Roo.log(records[i]);
6478             //Roo.log(this.store.getAt(rowIndex+i));
6479             this.insertRow(this.store, rowIndex + i, false);
6480             return;
6481         }
6482         
6483     },
6484     
6485     
6486     refreshRow : function(record){
6487         var ds = this.store, index;
6488         if(typeof record == 'number'){
6489             index = record;
6490             record = ds.getAt(index);
6491         }else{
6492             index = ds.indexOf(record);
6493         }
6494         this.insertRow(ds, index, true);
6495         this.autoSize();
6496         this.onRemove(ds, record, index+1, true);
6497         this.autoSize();
6498         //this.syncRowHeights(index, index);
6499         //this.layout();
6500         this.fireEvent("rowupdated", this, index, record);
6501     },
6502     
6503     insertRow : function(dm, rowIndex, isUpdate){
6504         
6505         if(!isUpdate){
6506             this.fireEvent("beforerowsinserted", this, rowIndex);
6507         }
6508             //var s = this.getScrollState();
6509         var row = this.renderRow(this.cm, this.store, rowIndex);
6510         // insert before rowIndex..
6511         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6512         
6513         var _this = this;
6514                 
6515         if(row.cellObjects.length){
6516             Roo.each(row.cellObjects, function(r){
6517                 _this.renderCellObject(r);
6518             })
6519         }
6520             
6521         if(!isUpdate){
6522             this.fireEvent("rowsinserted", this, rowIndex);
6523             //this.syncRowHeights(firstRow, lastRow);
6524             //this.stripeRows(firstRow);
6525             //this.layout();
6526         }
6527         
6528     },
6529     
6530     
6531     getRowDom : function(rowIndex)
6532     {
6533         var rows = this.el.select('tbody > tr', true).elements;
6534         
6535         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6536         
6537     },
6538     // returns the object tree for a tr..
6539   
6540     
6541     renderRow : function(cm, ds, rowIndex) 
6542     {
6543         
6544         var d = ds.getAt(rowIndex);
6545         
6546         var row = {
6547             tag : 'tr',
6548             cn : []
6549         };
6550             
6551         var cellObjects = [];
6552         
6553         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6554             var config = cm.config[i];
6555             
6556             var renderer = cm.getRenderer(i);
6557             var value = '';
6558             var id = false;
6559             
6560             if(typeof(renderer) !== 'undefined'){
6561                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6562             }
6563             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6564             // and are rendered into the cells after the row is rendered - using the id for the element.
6565             
6566             if(typeof(value) === 'object'){
6567                 id = Roo.id();
6568                 cellObjects.push({
6569                     container : id,
6570                     cfg : value 
6571                 })
6572             }
6573             
6574             var rowcfg = {
6575                 record: d,
6576                 rowIndex : rowIndex,
6577                 colIndex : i,
6578                 rowClass : ''
6579             };
6580
6581             this.fireEvent('rowclass', this, rowcfg);
6582             
6583             var td = {
6584                 tag: 'td',
6585                 cls : rowcfg.rowClass,
6586                 style: '',
6587                 html: (typeof(value) === 'object') ? '' : value
6588             };
6589             
6590             if (id) {
6591                 td.id = id;
6592             }
6593             
6594             if(typeof(config.colspan) != 'undefined'){
6595                 td.colspan = config.colspan;
6596             }
6597             
6598             if(typeof(config.hidden) != 'undefined' && config.hidden){
6599                 td.style += ' display:none;';
6600             }
6601             
6602             if(typeof(config.align) != 'undefined' && config.align.length){
6603                 td.style += ' text-align:' + config.align + ';';
6604             }
6605             
6606             if(typeof(config.width) != 'undefined'){
6607                 td.style += ' width:' +  config.width + 'px;';
6608             }
6609             
6610             if(typeof(config.cursor) != 'undefined'){
6611                 td.style += ' cursor:' +  config.cursor + ';';
6612             }
6613             
6614             if(typeof(config.cls) != 'undefined'){
6615                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6616             }
6617             
6618             ['xs','sm','md','lg'].map(function(size){
6619                 
6620                 if(typeof(config[size]) == 'undefined'){
6621                     return;
6622                 }
6623                 
6624                 if (!config[size]) { // 0 = hidden
6625                     td.cls += ' hidden-' + size;
6626                     return;
6627                 }
6628                 
6629                 td.cls += ' col-' + size + '-' + config[size];
6630
6631             });
6632              
6633             row.cn.push(td);
6634            
6635         }
6636         
6637         row.cellObjects = cellObjects;
6638         
6639         return row;
6640           
6641     },
6642     
6643     
6644     
6645     onBeforeLoad : function()
6646     {
6647         //Roo.log('ds onBeforeLoad');
6648         
6649         //this.clear();
6650         
6651         //if(this.loadMask){
6652         //    this.maskEl.show();
6653         //}
6654     },
6655      /**
6656      * Remove all rows
6657      */
6658     clear : function()
6659     {
6660         this.el.select('tbody', true).first().dom.innerHTML = '';
6661     },
6662     /**
6663      * Show or hide a row.
6664      * @param {Number} rowIndex to show or hide
6665      * @param {Boolean} state hide
6666      */
6667     setRowVisibility : function(rowIndex, state)
6668     {
6669         var bt = this.mainBody.dom;
6670         
6671         var rows = this.el.select('tbody > tr', true).elements;
6672         
6673         if(typeof(rows[rowIndex]) == 'undefined'){
6674             return;
6675         }
6676         rows[rowIndex].dom.style.display = state ? '' : 'none';
6677     },
6678     
6679     
6680     getSelectionModel : function(){
6681         if(!this.selModel){
6682             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6683         }
6684         return this.selModel;
6685     },
6686     /*
6687      * Render the Roo.bootstrap object from renderder
6688      */
6689     renderCellObject : function(r)
6690     {
6691         var _this = this;
6692         
6693         var t = r.cfg.render(r.container);
6694         
6695         if(r.cfg.cn){
6696             Roo.each(r.cfg.cn, function(c){
6697                 var child = {
6698                     container: t.getChildContainer(),
6699                     cfg: c
6700                 };
6701                 _this.renderCellObject(child);
6702             })
6703         }
6704     },
6705     
6706     getRowIndex : function(row)
6707     {
6708         var rowIndex = -1;
6709         
6710         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6711             if(el != row){
6712                 return;
6713             }
6714             
6715             rowIndex = index;
6716         });
6717         
6718         return rowIndex;
6719     },
6720      /**
6721      * Returns the grid's underlying element = used by panel.Grid
6722      * @return {Element} The element
6723      */
6724     getGridEl : function(){
6725         return this.el;
6726     },
6727      /**
6728      * Forces a resize - used by panel.Grid
6729      * @return {Element} The element
6730      */
6731     autoSize : function()
6732     {
6733         //var ctr = Roo.get(this.container.dom.parentElement);
6734         var ctr = Roo.get(this.el.dom);
6735         
6736         var thd = this.getGridEl().select('thead',true).first();
6737         var tbd = this.getGridEl().select('tbody', true).first();
6738         var tfd = this.getGridEl().select('tfoot', true).first();
6739         
6740         var cw = ctr.getWidth();
6741         
6742         if (tbd) {
6743             
6744             tbd.setSize(ctr.getWidth(),
6745                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6746             );
6747             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6748             cw -= barsize;
6749         }
6750         cw = Math.max(cw, this.totalWidth);
6751         this.getGridEl().select('tr',true).setWidth(cw);
6752         // resize 'expandable coloumn?
6753         
6754         return; // we doe not have a view in this design..
6755         
6756     },
6757     onBodyScroll: function()
6758     {
6759         
6760         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6761         this.mainHead.setStyle({
6762                     'position' : 'relative',
6763                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6764         });
6765         
6766         
6767     }
6768 });
6769
6770  
6771
6772  /*
6773  * - LGPL
6774  *
6775  * table cell
6776  * 
6777  */
6778
6779 /**
6780  * @class Roo.bootstrap.TableCell
6781  * @extends Roo.bootstrap.Component
6782  * Bootstrap TableCell class
6783  * @cfg {String} html cell contain text
6784  * @cfg {String} cls cell class
6785  * @cfg {String} tag cell tag (td|th) default td
6786  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6787  * @cfg {String} align Aligns the content in a cell
6788  * @cfg {String} axis Categorizes cells
6789  * @cfg {String} bgcolor Specifies the background color of a cell
6790  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6791  * @cfg {Number} colspan Specifies the number of columns a cell should span
6792  * @cfg {String} headers Specifies one or more header cells a cell is related to
6793  * @cfg {Number} height Sets the height of a cell
6794  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6795  * @cfg {Number} rowspan Sets the number of rows a cell should span
6796  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6797  * @cfg {String} valign Vertical aligns the content in a cell
6798  * @cfg {Number} width Specifies the width of a cell
6799  * 
6800  * @constructor
6801  * Create a new TableCell
6802  * @param {Object} config The config object
6803  */
6804
6805 Roo.bootstrap.TableCell = function(config){
6806     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6807 };
6808
6809 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6810     
6811     html: false,
6812     cls: false,
6813     tag: false,
6814     abbr: false,
6815     align: false,
6816     axis: false,
6817     bgcolor: false,
6818     charoff: false,
6819     colspan: false,
6820     headers: false,
6821     height: false,
6822     nowrap: false,
6823     rowspan: false,
6824     scope: false,
6825     valign: false,
6826     width: false,
6827     
6828     
6829     getAutoCreate : function(){
6830         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6831         
6832         cfg = {
6833             tag: 'td'
6834         };
6835         
6836         if(this.tag){
6837             cfg.tag = this.tag;
6838         }
6839         
6840         if (this.html) {
6841             cfg.html=this.html
6842         }
6843         if (this.cls) {
6844             cfg.cls=this.cls
6845         }
6846         if (this.abbr) {
6847             cfg.abbr=this.abbr
6848         }
6849         if (this.align) {
6850             cfg.align=this.align
6851         }
6852         if (this.axis) {
6853             cfg.axis=this.axis
6854         }
6855         if (this.bgcolor) {
6856             cfg.bgcolor=this.bgcolor
6857         }
6858         if (this.charoff) {
6859             cfg.charoff=this.charoff
6860         }
6861         if (this.colspan) {
6862             cfg.colspan=this.colspan
6863         }
6864         if (this.headers) {
6865             cfg.headers=this.headers
6866         }
6867         if (this.height) {
6868             cfg.height=this.height
6869         }
6870         if (this.nowrap) {
6871             cfg.nowrap=this.nowrap
6872         }
6873         if (this.rowspan) {
6874             cfg.rowspan=this.rowspan
6875         }
6876         if (this.scope) {
6877             cfg.scope=this.scope
6878         }
6879         if (this.valign) {
6880             cfg.valign=this.valign
6881         }
6882         if (this.width) {
6883             cfg.width=this.width
6884         }
6885         
6886         
6887         return cfg;
6888     }
6889    
6890 });
6891
6892  
6893
6894  /*
6895  * - LGPL
6896  *
6897  * table row
6898  * 
6899  */
6900
6901 /**
6902  * @class Roo.bootstrap.TableRow
6903  * @extends Roo.bootstrap.Component
6904  * Bootstrap TableRow class
6905  * @cfg {String} cls row class
6906  * @cfg {String} align Aligns the content in a table row
6907  * @cfg {String} bgcolor Specifies a background color for a table row
6908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6909  * @cfg {String} valign Vertical aligns the content in a table row
6910  * 
6911  * @constructor
6912  * Create a new TableRow
6913  * @param {Object} config The config object
6914  */
6915
6916 Roo.bootstrap.TableRow = function(config){
6917     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6918 };
6919
6920 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6921     
6922     cls: false,
6923     align: false,
6924     bgcolor: false,
6925     charoff: false,
6926     valign: false,
6927     
6928     getAutoCreate : function(){
6929         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6930         
6931         cfg = {
6932             tag: 'tr'
6933         };
6934             
6935         if(this.cls){
6936             cfg.cls = this.cls;
6937         }
6938         if(this.align){
6939             cfg.align = this.align;
6940         }
6941         if(this.bgcolor){
6942             cfg.bgcolor = this.bgcolor;
6943         }
6944         if(this.charoff){
6945             cfg.charoff = this.charoff;
6946         }
6947         if(this.valign){
6948             cfg.valign = this.valign;
6949         }
6950         
6951         return cfg;
6952     }
6953    
6954 });
6955
6956  
6957
6958  /*
6959  * - LGPL
6960  *
6961  * table body
6962  * 
6963  */
6964
6965 /**
6966  * @class Roo.bootstrap.TableBody
6967  * @extends Roo.bootstrap.Component
6968  * Bootstrap TableBody class
6969  * @cfg {String} cls element class
6970  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6971  * @cfg {String} align Aligns the content inside the element
6972  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6973  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6974  * 
6975  * @constructor
6976  * Create a new TableBody
6977  * @param {Object} config The config object
6978  */
6979
6980 Roo.bootstrap.TableBody = function(config){
6981     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6982 };
6983
6984 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6985     
6986     cls: false,
6987     tag: false,
6988     align: false,
6989     charoff: false,
6990     valign: false,
6991     
6992     getAutoCreate : function(){
6993         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6994         
6995         cfg = {
6996             tag: 'tbody'
6997         };
6998             
6999         if (this.cls) {
7000             cfg.cls=this.cls
7001         }
7002         if(this.tag){
7003             cfg.tag = this.tag;
7004         }
7005         
7006         if(this.align){
7007             cfg.align = this.align;
7008         }
7009         if(this.charoff){
7010             cfg.charoff = this.charoff;
7011         }
7012         if(this.valign){
7013             cfg.valign = this.valign;
7014         }
7015         
7016         return cfg;
7017     }
7018     
7019     
7020 //    initEvents : function()
7021 //    {
7022 //        
7023 //        if(!this.store){
7024 //            return;
7025 //        }
7026 //        
7027 //        this.store = Roo.factory(this.store, Roo.data);
7028 //        this.store.on('load', this.onLoad, this);
7029 //        
7030 //        this.store.load();
7031 //        
7032 //    },
7033 //    
7034 //    onLoad: function () 
7035 //    {   
7036 //        this.fireEvent('load', this);
7037 //    }
7038 //    
7039 //   
7040 });
7041
7042  
7043
7044  /*
7045  * Based on:
7046  * Ext JS Library 1.1.1
7047  * Copyright(c) 2006-2007, Ext JS, LLC.
7048  *
7049  * Originally Released Under LGPL - original licence link has changed is not relivant.
7050  *
7051  * Fork - LGPL
7052  * <script type="text/javascript">
7053  */
7054
7055 // as we use this in bootstrap.
7056 Roo.namespace('Roo.form');
7057  /**
7058  * @class Roo.form.Action
7059  * Internal Class used to handle form actions
7060  * @constructor
7061  * @param {Roo.form.BasicForm} el The form element or its id
7062  * @param {Object} config Configuration options
7063  */
7064
7065  
7066  
7067 // define the action interface
7068 Roo.form.Action = function(form, options){
7069     this.form = form;
7070     this.options = options || {};
7071 };
7072 /**
7073  * Client Validation Failed
7074  * @const 
7075  */
7076 Roo.form.Action.CLIENT_INVALID = 'client';
7077 /**
7078  * Server Validation Failed
7079  * @const 
7080  */
7081 Roo.form.Action.SERVER_INVALID = 'server';
7082  /**
7083  * Connect to Server Failed
7084  * @const 
7085  */
7086 Roo.form.Action.CONNECT_FAILURE = 'connect';
7087 /**
7088  * Reading Data from Server Failed
7089  * @const 
7090  */
7091 Roo.form.Action.LOAD_FAILURE = 'load';
7092
7093 Roo.form.Action.prototype = {
7094     type : 'default',
7095     failureType : undefined,
7096     response : undefined,
7097     result : undefined,
7098
7099     // interface method
7100     run : function(options){
7101
7102     },
7103
7104     // interface method
7105     success : function(response){
7106
7107     },
7108
7109     // interface method
7110     handleResponse : function(response){
7111
7112     },
7113
7114     // default connection failure
7115     failure : function(response){
7116         
7117         this.response = response;
7118         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7119         this.form.afterAction(this, false);
7120     },
7121
7122     processResponse : function(response){
7123         this.response = response;
7124         if(!response.responseText){
7125             return true;
7126         }
7127         this.result = this.handleResponse(response);
7128         return this.result;
7129     },
7130
7131     // utility functions used internally
7132     getUrl : function(appendParams){
7133         var url = this.options.url || this.form.url || this.form.el.dom.action;
7134         if(appendParams){
7135             var p = this.getParams();
7136             if(p){
7137                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7138             }
7139         }
7140         return url;
7141     },
7142
7143     getMethod : function(){
7144         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7145     },
7146
7147     getParams : function(){
7148         var bp = this.form.baseParams;
7149         var p = this.options.params;
7150         if(p){
7151             if(typeof p == "object"){
7152                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7153             }else if(typeof p == 'string' && bp){
7154                 p += '&' + Roo.urlEncode(bp);
7155             }
7156         }else if(bp){
7157             p = Roo.urlEncode(bp);
7158         }
7159         return p;
7160     },
7161
7162     createCallback : function(){
7163         return {
7164             success: this.success,
7165             failure: this.failure,
7166             scope: this,
7167             timeout: (this.form.timeout*1000),
7168             upload: this.form.fileUpload ? this.success : undefined
7169         };
7170     }
7171 };
7172
7173 Roo.form.Action.Submit = function(form, options){
7174     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7175 };
7176
7177 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7178     type : 'submit',
7179
7180     haveProgress : false,
7181     uploadComplete : false,
7182     
7183     // uploadProgress indicator.
7184     uploadProgress : function()
7185     {
7186         if (!this.form.progressUrl) {
7187             return;
7188         }
7189         
7190         if (!this.haveProgress) {
7191             Roo.MessageBox.progress("Uploading", "Uploading");
7192         }
7193         if (this.uploadComplete) {
7194            Roo.MessageBox.hide();
7195            return;
7196         }
7197         
7198         this.haveProgress = true;
7199    
7200         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7201         
7202         var c = new Roo.data.Connection();
7203         c.request({
7204             url : this.form.progressUrl,
7205             params: {
7206                 id : uid
7207             },
7208             method: 'GET',
7209             success : function(req){
7210                //console.log(data);
7211                 var rdata = false;
7212                 var edata;
7213                 try  {
7214                    rdata = Roo.decode(req.responseText)
7215                 } catch (e) {
7216                     Roo.log("Invalid data from server..");
7217                     Roo.log(edata);
7218                     return;
7219                 }
7220                 if (!rdata || !rdata.success) {
7221                     Roo.log(rdata);
7222                     Roo.MessageBox.alert(Roo.encode(rdata));
7223                     return;
7224                 }
7225                 var data = rdata.data;
7226                 
7227                 if (this.uploadComplete) {
7228                    Roo.MessageBox.hide();
7229                    return;
7230                 }
7231                    
7232                 if (data){
7233                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7234                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7235                     );
7236                 }
7237                 this.uploadProgress.defer(2000,this);
7238             },
7239        
7240             failure: function(data) {
7241                 Roo.log('progress url failed ');
7242                 Roo.log(data);
7243             },
7244             scope : this
7245         });
7246            
7247     },
7248     
7249     
7250     run : function()
7251     {
7252         // run get Values on the form, so it syncs any secondary forms.
7253         this.form.getValues();
7254         
7255         var o = this.options;
7256         var method = this.getMethod();
7257         var isPost = method == 'POST';
7258         if(o.clientValidation === false || this.form.isValid()){
7259             
7260             if (this.form.progressUrl) {
7261                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7262                     (new Date() * 1) + '' + Math.random());
7263                     
7264             } 
7265             
7266             
7267             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7268                 form:this.form.el.dom,
7269                 url:this.getUrl(!isPost),
7270                 method: method,
7271                 params:isPost ? this.getParams() : null,
7272                 isUpload: this.form.fileUpload
7273             }));
7274             
7275             this.uploadProgress();
7276
7277         }else if (o.clientValidation !== false){ // client validation failed
7278             this.failureType = Roo.form.Action.CLIENT_INVALID;
7279             this.form.afterAction(this, false);
7280         }
7281     },
7282
7283     success : function(response)
7284     {
7285         this.uploadComplete= true;
7286         if (this.haveProgress) {
7287             Roo.MessageBox.hide();
7288         }
7289         
7290         
7291         var result = this.processResponse(response);
7292         if(result === true || result.success){
7293             this.form.afterAction(this, true);
7294             return;
7295         }
7296         if(result.errors){
7297             this.form.markInvalid(result.errors);
7298             this.failureType = Roo.form.Action.SERVER_INVALID;
7299         }
7300         this.form.afterAction(this, false);
7301     },
7302     failure : function(response)
7303     {
7304         this.uploadComplete= true;
7305         if (this.haveProgress) {
7306             Roo.MessageBox.hide();
7307         }
7308         
7309         this.response = response;
7310         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7311         this.form.afterAction(this, false);
7312     },
7313     
7314     handleResponse : function(response){
7315         if(this.form.errorReader){
7316             var rs = this.form.errorReader.read(response);
7317             var errors = [];
7318             if(rs.records){
7319                 for(var i = 0, len = rs.records.length; i < len; i++) {
7320                     var r = rs.records[i];
7321                     errors[i] = r.data;
7322                 }
7323             }
7324             if(errors.length < 1){
7325                 errors = null;
7326             }
7327             return {
7328                 success : rs.success,
7329                 errors : errors
7330             };
7331         }
7332         var ret = false;
7333         try {
7334             ret = Roo.decode(response.responseText);
7335         } catch (e) {
7336             ret = {
7337                 success: false,
7338                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7339                 errors : []
7340             };
7341         }
7342         return ret;
7343         
7344     }
7345 });
7346
7347
7348 Roo.form.Action.Load = function(form, options){
7349     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7350     this.reader = this.form.reader;
7351 };
7352
7353 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7354     type : 'load',
7355
7356     run : function(){
7357         
7358         Roo.Ajax.request(Roo.apply(
7359                 this.createCallback(), {
7360                     method:this.getMethod(),
7361                     url:this.getUrl(false),
7362                     params:this.getParams()
7363         }));
7364     },
7365
7366     success : function(response){
7367         
7368         var result = this.processResponse(response);
7369         if(result === true || !result.success || !result.data){
7370             this.failureType = Roo.form.Action.LOAD_FAILURE;
7371             this.form.afterAction(this, false);
7372             return;
7373         }
7374         this.form.clearInvalid();
7375         this.form.setValues(result.data);
7376         this.form.afterAction(this, true);
7377     },
7378
7379     handleResponse : function(response){
7380         if(this.form.reader){
7381             var rs = this.form.reader.read(response);
7382             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7383             return {
7384                 success : rs.success,
7385                 data : data
7386             };
7387         }
7388         return Roo.decode(response.responseText);
7389     }
7390 });
7391
7392 Roo.form.Action.ACTION_TYPES = {
7393     'load' : Roo.form.Action.Load,
7394     'submit' : Roo.form.Action.Submit
7395 };/*
7396  * - LGPL
7397  *
7398  * form
7399  * 
7400  */
7401
7402 /**
7403  * @class Roo.bootstrap.Form
7404  * @extends Roo.bootstrap.Component
7405  * Bootstrap Form class
7406  * @cfg {String} method  GET | POST (default POST)
7407  * @cfg {String} labelAlign top | left (default top)
7408  * @cfg {String} align left  | right - for navbars
7409  * @cfg {Boolean} loadMask load mask when submit (default true)
7410
7411  * 
7412  * @constructor
7413  * Create a new Form
7414  * @param {Object} config The config object
7415  */
7416
7417
7418 Roo.bootstrap.Form = function(config){
7419     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7420     this.addEvents({
7421         /**
7422          * @event clientvalidation
7423          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7424          * @param {Form} this
7425          * @param {Boolean} valid true if the form has passed client-side validation
7426          */
7427         clientvalidation: true,
7428         /**
7429          * @event beforeaction
7430          * Fires before any action is performed. Return false to cancel the action.
7431          * @param {Form} this
7432          * @param {Action} action The action to be performed
7433          */
7434         beforeaction: true,
7435         /**
7436          * @event actionfailed
7437          * Fires when an action fails.
7438          * @param {Form} this
7439          * @param {Action} action The action that failed
7440          */
7441         actionfailed : true,
7442         /**
7443          * @event actioncomplete
7444          * Fires when an action is completed.
7445          * @param {Form} this
7446          * @param {Action} action The action that completed
7447          */
7448         actioncomplete : true
7449     });
7450     
7451 };
7452
7453 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7454       
7455      /**
7456      * @cfg {String} method
7457      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7458      */
7459     method : 'POST',
7460     /**
7461      * @cfg {String} url
7462      * The URL to use for form actions if one isn't supplied in the action options.
7463      */
7464     /**
7465      * @cfg {Boolean} fileUpload
7466      * Set to true if this form is a file upload.
7467      */
7468      
7469     /**
7470      * @cfg {Object} baseParams
7471      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7472      */
7473       
7474     /**
7475      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7476      */
7477     timeout: 30,
7478     /**
7479      * @cfg {Sting} align (left|right) for navbar forms
7480      */
7481     align : 'left',
7482
7483     // private
7484     activeAction : null,
7485  
7486     /**
7487      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7488      * element by passing it or its id or mask the form itself by passing in true.
7489      * @type Mixed
7490      */
7491     waitMsgTarget : false,
7492     
7493     loadMask : true,
7494     
7495     getAutoCreate : function(){
7496         
7497         var cfg = {
7498             tag: 'form',
7499             method : this.method || 'POST',
7500             id : this.id || Roo.id(),
7501             cls : ''
7502         };
7503         if (this.parent().xtype.match(/^Nav/)) {
7504             cfg.cls = 'navbar-form navbar-' + this.align;
7505             
7506         }
7507         
7508         if (this.labelAlign == 'left' ) {
7509             cfg.cls += ' form-horizontal';
7510         }
7511         
7512         
7513         return cfg;
7514     },
7515     initEvents : function()
7516     {
7517         this.el.on('submit', this.onSubmit, this);
7518         // this was added as random key presses on the form where triggering form submit.
7519         this.el.on('keypress', function(e) {
7520             if (e.getCharCode() != 13) {
7521                 return true;
7522             }
7523             // we might need to allow it for textareas.. and some other items.
7524             // check e.getTarget().
7525             
7526             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7527                 return true;
7528             }
7529         
7530             Roo.log("keypress blocked");
7531             
7532             e.preventDefault();
7533             return false;
7534         });
7535         
7536     },
7537     // private
7538     onSubmit : function(e){
7539         e.stopEvent();
7540     },
7541     
7542      /**
7543      * Returns true if client-side validation on the form is successful.
7544      * @return Boolean
7545      */
7546     isValid : function(){
7547         var items = this.getItems();
7548         var valid = true;
7549         items.each(function(f){
7550            if(!f.validate()){
7551                valid = false;
7552                
7553            }
7554         });
7555         return valid;
7556     },
7557     /**
7558      * Returns true if any fields in this form have changed since their original load.
7559      * @return Boolean
7560      */
7561     isDirty : function(){
7562         var dirty = false;
7563         var items = this.getItems();
7564         items.each(function(f){
7565            if(f.isDirty()){
7566                dirty = true;
7567                return false;
7568            }
7569            return true;
7570         });
7571         return dirty;
7572     },
7573      /**
7574      * Performs a predefined action (submit or load) or custom actions you define on this form.
7575      * @param {String} actionName The name of the action type
7576      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7577      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7578      * accept other config options):
7579      * <pre>
7580 Property          Type             Description
7581 ----------------  ---------------  ----------------------------------------------------------------------------------
7582 url               String           The url for the action (defaults to the form's url)
7583 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7584 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7585 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7586                                    validate the form on the client (defaults to false)
7587      * </pre>
7588      * @return {BasicForm} this
7589      */
7590     doAction : function(action, options){
7591         if(typeof action == 'string'){
7592             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7593         }
7594         if(this.fireEvent('beforeaction', this, action) !== false){
7595             this.beforeAction(action);
7596             action.run.defer(100, action);
7597         }
7598         return this;
7599     },
7600     
7601     // private
7602     beforeAction : function(action){
7603         var o = action.options;
7604         
7605         if(this.loadMask){
7606             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7607         }
7608         // not really supported yet.. ??
7609         
7610         //if(this.waitMsgTarget === true){
7611         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7612         //}else if(this.waitMsgTarget){
7613         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7614         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7615         //}else {
7616         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7617        // }
7618          
7619     },
7620
7621     // private
7622     afterAction : function(action, success){
7623         this.activeAction = null;
7624         var o = action.options;
7625         
7626         //if(this.waitMsgTarget === true){
7627             this.el.unmask();
7628         //}else if(this.waitMsgTarget){
7629         //    this.waitMsgTarget.unmask();
7630         //}else{
7631         //    Roo.MessageBox.updateProgress(1);
7632         //    Roo.MessageBox.hide();
7633        // }
7634         // 
7635         if(success){
7636             if(o.reset){
7637                 this.reset();
7638             }
7639             Roo.callback(o.success, o.scope, [this, action]);
7640             this.fireEvent('actioncomplete', this, action);
7641             
7642         }else{
7643             
7644             // failure condition..
7645             // we have a scenario where updates need confirming.
7646             // eg. if a locking scenario exists..
7647             // we look for { errors : { needs_confirm : true }} in the response.
7648             if (
7649                 (typeof(action.result) != 'undefined')  &&
7650                 (typeof(action.result.errors) != 'undefined')  &&
7651                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7652            ){
7653                 var _t = this;
7654                 Roo.log("not supported yet");
7655                  /*
7656                 
7657                 Roo.MessageBox.confirm(
7658                     "Change requires confirmation",
7659                     action.result.errorMsg,
7660                     function(r) {
7661                         if (r != 'yes') {
7662                             return;
7663                         }
7664                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7665                     }
7666                     
7667                 );
7668                 */
7669                 
7670                 
7671                 return;
7672             }
7673             
7674             Roo.callback(o.failure, o.scope, [this, action]);
7675             // show an error message if no failed handler is set..
7676             if (!this.hasListener('actionfailed')) {
7677                 Roo.log("need to add dialog support");
7678                 /*
7679                 Roo.MessageBox.alert("Error",
7680                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7681                         action.result.errorMsg :
7682                         "Saving Failed, please check your entries or try again"
7683                 );
7684                 */
7685             }
7686             
7687             this.fireEvent('actionfailed', this, action);
7688         }
7689         
7690     },
7691     /**
7692      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7693      * @param {String} id The value to search for
7694      * @return Field
7695      */
7696     findField : function(id){
7697         var items = this.getItems();
7698         var field = items.get(id);
7699         if(!field){
7700              items.each(function(f){
7701                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7702                     field = f;
7703                     return false;
7704                 }
7705                 return true;
7706             });
7707         }
7708         return field || null;
7709     },
7710      /**
7711      * Mark fields in this form invalid in bulk.
7712      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7713      * @return {BasicForm} this
7714      */
7715     markInvalid : function(errors){
7716         if(errors instanceof Array){
7717             for(var i = 0, len = errors.length; i < len; i++){
7718                 var fieldError = errors[i];
7719                 var f = this.findField(fieldError.id);
7720                 if(f){
7721                     f.markInvalid(fieldError.msg);
7722                 }
7723             }
7724         }else{
7725             var field, id;
7726             for(id in errors){
7727                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7728                     field.markInvalid(errors[id]);
7729                 }
7730             }
7731         }
7732         //Roo.each(this.childForms || [], function (f) {
7733         //    f.markInvalid(errors);
7734         //});
7735         
7736         return this;
7737     },
7738
7739     /**
7740      * Set values for fields in this form in bulk.
7741      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7742      * @return {BasicForm} this
7743      */
7744     setValues : function(values){
7745         if(values instanceof Array){ // array of objects
7746             for(var i = 0, len = values.length; i < len; i++){
7747                 var v = values[i];
7748                 var f = this.findField(v.id);
7749                 if(f){
7750                     f.setValue(v.value);
7751                     if(this.trackResetOnLoad){
7752                         f.originalValue = f.getValue();
7753                     }
7754                 }
7755             }
7756         }else{ // object hash
7757             var field, id;
7758             for(id in values){
7759                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7760                     
7761                     if (field.setFromData && 
7762                         field.valueField && 
7763                         field.displayField &&
7764                         // combos' with local stores can 
7765                         // be queried via setValue()
7766                         // to set their value..
7767                         (field.store && !field.store.isLocal)
7768                         ) {
7769                         // it's a combo
7770                         var sd = { };
7771                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7772                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7773                         field.setFromData(sd);
7774                         
7775                     } else {
7776                         field.setValue(values[id]);
7777                     }
7778                     
7779                     
7780                     if(this.trackResetOnLoad){
7781                         field.originalValue = field.getValue();
7782                     }
7783                 }
7784             }
7785         }
7786          
7787         //Roo.each(this.childForms || [], function (f) {
7788         //    f.setValues(values);
7789         //});
7790                 
7791         return this;
7792     },
7793
7794     /**
7795      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7796      * they are returned as an array.
7797      * @param {Boolean} asString
7798      * @return {Object}
7799      */
7800     getValues : function(asString){
7801         //if (this.childForms) {
7802             // copy values from the child forms
7803         //    Roo.each(this.childForms, function (f) {
7804         //        this.setValues(f.getValues());
7805         //    }, this);
7806         //}
7807         
7808         
7809         
7810         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7811         if(asString === true){
7812             return fs;
7813         }
7814         return Roo.urlDecode(fs);
7815     },
7816     
7817     /**
7818      * Returns the fields in this form as an object with key/value pairs. 
7819      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7820      * @return {Object}
7821      */
7822     getFieldValues : function(with_hidden)
7823     {
7824         var items = this.getItems();
7825         var ret = {};
7826         items.each(function(f){
7827             if (!f.getName()) {
7828                 return;
7829             }
7830             var v = f.getValue();
7831             if (f.inputType =='radio') {
7832                 if (typeof(ret[f.getName()]) == 'undefined') {
7833                     ret[f.getName()] = ''; // empty..
7834                 }
7835                 
7836                 if (!f.el.dom.checked) {
7837                     return;
7838                     
7839                 }
7840                 v = f.el.dom.value;
7841                 
7842             }
7843             
7844             // not sure if this supported any more..
7845             if ((typeof(v) == 'object') && f.getRawValue) {
7846                 v = f.getRawValue() ; // dates..
7847             }
7848             // combo boxes where name != hiddenName...
7849             if (f.name != f.getName()) {
7850                 ret[f.name] = f.getRawValue();
7851             }
7852             ret[f.getName()] = v;
7853         });
7854         
7855         return ret;
7856     },
7857
7858     /**
7859      * Clears all invalid messages in this form.
7860      * @return {BasicForm} this
7861      */
7862     clearInvalid : function(){
7863         var items = this.getItems();
7864         
7865         items.each(function(f){
7866            f.clearInvalid();
7867         });
7868         
7869         
7870         
7871         return this;
7872     },
7873
7874     /**
7875      * Resets this form.
7876      * @return {BasicForm} this
7877      */
7878     reset : function(){
7879         var items = this.getItems();
7880         items.each(function(f){
7881             f.reset();
7882         });
7883         
7884         Roo.each(this.childForms || [], function (f) {
7885             f.reset();
7886         });
7887        
7888         
7889         return this;
7890     },
7891     getItems : function()
7892     {
7893         var r=new Roo.util.MixedCollection(false, function(o){
7894             return o.id || (o.id = Roo.id());
7895         });
7896         var iter = function(el) {
7897             if (el.inputEl) {
7898                 r.add(el);
7899             }
7900             if (!el.items) {
7901                 return;
7902             }
7903             Roo.each(el.items,function(e) {
7904                 iter(e);
7905             });
7906             
7907             
7908         };
7909         
7910         iter(this);
7911         return r;
7912         
7913         
7914         
7915         
7916     }
7917     
7918 });
7919
7920  
7921 /*
7922  * Based on:
7923  * Ext JS Library 1.1.1
7924  * Copyright(c) 2006-2007, Ext JS, LLC.
7925  *
7926  * Originally Released Under LGPL - original licence link has changed is not relivant.
7927  *
7928  * Fork - LGPL
7929  * <script type="text/javascript">
7930  */
7931 /**
7932  * @class Roo.form.VTypes
7933  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7934  * @singleton
7935  */
7936 Roo.form.VTypes = function(){
7937     // closure these in so they are only created once.
7938     var alpha = /^[a-zA-Z_]+$/;
7939     var alphanum = /^[a-zA-Z0-9_]+$/;
7940     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7941     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7942
7943     // All these messages and functions are configurable
7944     return {
7945         /**
7946          * The function used to validate email addresses
7947          * @param {String} value The email address
7948          */
7949         'email' : function(v){
7950             return email.test(v);
7951         },
7952         /**
7953          * The error text to display when the email validation function returns false
7954          * @type String
7955          */
7956         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7957         /**
7958          * The keystroke filter mask to be applied on email input
7959          * @type RegExp
7960          */
7961         'emailMask' : /[a-z0-9_\.\-@]/i,
7962
7963         /**
7964          * The function used to validate URLs
7965          * @param {String} value The URL
7966          */
7967         'url' : function(v){
7968             return url.test(v);
7969         },
7970         /**
7971          * The error text to display when the url validation function returns false
7972          * @type String
7973          */
7974         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7975         
7976         /**
7977          * The function used to validate alpha values
7978          * @param {String} value The value
7979          */
7980         'alpha' : function(v){
7981             return alpha.test(v);
7982         },
7983         /**
7984          * The error text to display when the alpha validation function returns false
7985          * @type String
7986          */
7987         'alphaText' : 'This field should only contain letters and _',
7988         /**
7989          * The keystroke filter mask to be applied on alpha input
7990          * @type RegExp
7991          */
7992         'alphaMask' : /[a-z_]/i,
7993
7994         /**
7995          * The function used to validate alphanumeric values
7996          * @param {String} value The value
7997          */
7998         'alphanum' : function(v){
7999             return alphanum.test(v);
8000         },
8001         /**
8002          * The error text to display when the alphanumeric validation function returns false
8003          * @type String
8004          */
8005         'alphanumText' : 'This field should only contain letters, numbers and _',
8006         /**
8007          * The keystroke filter mask to be applied on alphanumeric input
8008          * @type RegExp
8009          */
8010         'alphanumMask' : /[a-z0-9_]/i
8011     };
8012 }();/*
8013  * - LGPL
8014  *
8015  * Input
8016  * 
8017  */
8018
8019 /**
8020  * @class Roo.bootstrap.Input
8021  * @extends Roo.bootstrap.Component
8022  * Bootstrap Input class
8023  * @cfg {Boolean} disabled is it disabled
8024  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8025  * @cfg {String} name name of the input
8026  * @cfg {string} fieldLabel - the label associated
8027  * @cfg {string} placeholder - placeholder to put in text.
8028  * @cfg {string}  before - input group add on before
8029  * @cfg {string} after - input group add on after
8030  * @cfg {string} size - (lg|sm) or leave empty..
8031  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8032  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8033  * @cfg {Number} md colspan out of 12 for computer-sized screens
8034  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8035  * @cfg {string} value default value of the input
8036  * @cfg {Number} labelWidth set the width of label (0-12)
8037  * @cfg {String} labelAlign (top|left)
8038  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8039  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8040  * @cfg {String} indicatorpos (left|right) default left
8041
8042  * @cfg {String} align (left|center|right) Default left
8043  * @cfg {Boolean} forceFeedback (true|false) Default false
8044  * 
8045  * 
8046  * 
8047  * 
8048  * @constructor
8049  * Create a new Input
8050  * @param {Object} config The config object
8051  */
8052
8053 Roo.bootstrap.Input = function(config){
8054     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8055    
8056         this.addEvents({
8057             /**
8058              * @event focus
8059              * Fires when this field receives input focus.
8060              * @param {Roo.form.Field} this
8061              */
8062             focus : true,
8063             /**
8064              * @event blur
8065              * Fires when this field loses input focus.
8066              * @param {Roo.form.Field} this
8067              */
8068             blur : true,
8069             /**
8070              * @event specialkey
8071              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8072              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8073              * @param {Roo.form.Field} this
8074              * @param {Roo.EventObject} e The event object
8075              */
8076             specialkey : true,
8077             /**
8078              * @event change
8079              * Fires just before the field blurs if the field value has changed.
8080              * @param {Roo.form.Field} this
8081              * @param {Mixed} newValue The new value
8082              * @param {Mixed} oldValue The original value
8083              */
8084             change : true,
8085             /**
8086              * @event invalid
8087              * Fires after the field has been marked as invalid.
8088              * @param {Roo.form.Field} this
8089              * @param {String} msg The validation message
8090              */
8091             invalid : true,
8092             /**
8093              * @event valid
8094              * Fires after the field has been validated with no errors.
8095              * @param {Roo.form.Field} this
8096              */
8097             valid : true,
8098              /**
8099              * @event keyup
8100              * Fires after the key up
8101              * @param {Roo.form.Field} this
8102              * @param {Roo.EventObject}  e The event Object
8103              */
8104             keyup : true
8105         });
8106 };
8107
8108 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8109      /**
8110      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8111       automatic validation (defaults to "keyup").
8112      */
8113     validationEvent : "keyup",
8114      /**
8115      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8116      */
8117     validateOnBlur : true,
8118     /**
8119      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8120      */
8121     validationDelay : 250,
8122      /**
8123      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8124      */
8125     focusClass : "x-form-focus",  // not needed???
8126     
8127        
8128     /**
8129      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8130      */
8131     invalidClass : "has-warning",
8132     
8133     /**
8134      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8135      */
8136     validClass : "has-success",
8137     
8138     /**
8139      * @cfg {Boolean} hasFeedback (true|false) default true
8140      */
8141     hasFeedback : true,
8142     
8143     /**
8144      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8145      */
8146     invalidFeedbackClass : "glyphicon-warning-sign",
8147     
8148     /**
8149      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8150      */
8151     validFeedbackClass : "glyphicon-ok",
8152     
8153     /**
8154      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8155      */
8156     selectOnFocus : false,
8157     
8158      /**
8159      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8160      */
8161     maskRe : null,
8162        /**
8163      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8164      */
8165     vtype : null,
8166     
8167       /**
8168      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8169      */
8170     disableKeyFilter : false,
8171     
8172        /**
8173      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8174      */
8175     disabled : false,
8176      /**
8177      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8178      */
8179     allowBlank : true,
8180     /**
8181      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8182      */
8183     blankText : "This field is required",
8184     
8185      /**
8186      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8187      */
8188     minLength : 0,
8189     /**
8190      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8191      */
8192     maxLength : Number.MAX_VALUE,
8193     /**
8194      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8195      */
8196     minLengthText : "The minimum length for this field is {0}",
8197     /**
8198      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8199      */
8200     maxLengthText : "The maximum length for this field is {0}",
8201   
8202     
8203     /**
8204      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8205      * If available, this function will be called only after the basic validators all return true, and will be passed the
8206      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8207      */
8208     validator : null,
8209     /**
8210      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8211      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8212      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8213      */
8214     regex : null,
8215     /**
8216      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8217      */
8218     regexText : "",
8219     
8220     autocomplete: false,
8221     
8222     
8223     fieldLabel : '',
8224     inputType : 'text',
8225     
8226     name : false,
8227     placeholder: false,
8228     before : false,
8229     after : false,
8230     size : false,
8231     hasFocus : false,
8232     preventMark: false,
8233     isFormField : true,
8234     value : '',
8235     labelWidth : 2,
8236     labelAlign : false,
8237     readOnly : false,
8238     align : false,
8239     formatedValue : false,
8240     forceFeedback : false,
8241     
8242     indicatorpos : 'left',
8243     
8244     parentLabelAlign : function()
8245     {
8246         var parent = this;
8247         while (parent.parent()) {
8248             parent = parent.parent();
8249             if (typeof(parent.labelAlign) !='undefined') {
8250                 return parent.labelAlign;
8251             }
8252         }
8253         return 'left';
8254         
8255     },
8256     
8257     getAutoCreate : function()
8258     {
8259         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8260         
8261         var id = Roo.id();
8262         
8263         var cfg = {};
8264         
8265         if(this.inputType != 'hidden'){
8266             cfg.cls = 'form-group' //input-group
8267         }
8268         
8269         var input =  {
8270             tag: 'input',
8271             id : id,
8272             type : this.inputType,
8273             value : this.value,
8274             cls : 'form-control',
8275             placeholder : this.placeholder || '',
8276             autocomplete : this.autocomplete || 'new-password'
8277         };
8278         
8279         if(this.align){
8280             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8281         }
8282         
8283         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8284             input.maxLength = this.maxLength;
8285         }
8286         
8287         if (this.disabled) {
8288             input.disabled=true;
8289         }
8290         
8291         if (this.readOnly) {
8292             input.readonly=true;
8293         }
8294         
8295         if (this.name) {
8296             input.name = this.name;
8297         }
8298         
8299         if (this.size) {
8300             input.cls += ' input-' + this.size;
8301         }
8302         
8303         var settings=this;
8304         ['xs','sm','md','lg'].map(function(size){
8305             if (settings[size]) {
8306                 cfg.cls += ' col-' + size + '-' + settings[size];
8307             }
8308         });
8309         
8310         var inputblock = input;
8311         
8312         var feedback = {
8313             tag: 'span',
8314             cls: 'glyphicon form-control-feedback'
8315         };
8316             
8317         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8318             
8319             inputblock = {
8320                 cls : 'has-feedback',
8321                 cn :  [
8322                     input,
8323                     feedback
8324                 ] 
8325             };  
8326         }
8327         
8328         if (this.before || this.after) {
8329             
8330             inputblock = {
8331                 cls : 'input-group',
8332                 cn :  [] 
8333             };
8334             
8335             if (this.before && typeof(this.before) == 'string') {
8336                 
8337                 inputblock.cn.push({
8338                     tag :'span',
8339                     cls : 'roo-input-before input-group-addon',
8340                     html : this.before
8341                 });
8342             }
8343             if (this.before && typeof(this.before) == 'object') {
8344                 this.before = Roo.factory(this.before);
8345                 
8346                 inputblock.cn.push({
8347                     tag :'span',
8348                     cls : 'roo-input-before input-group-' +
8349                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8350                 });
8351             }
8352             
8353             inputblock.cn.push(input);
8354             
8355             if (this.after && typeof(this.after) == 'string') {
8356                 inputblock.cn.push({
8357                     tag :'span',
8358                     cls : 'roo-input-after input-group-addon',
8359                     html : this.after
8360                 });
8361             }
8362             if (this.after && typeof(this.after) == 'object') {
8363                 this.after = Roo.factory(this.after);
8364                 
8365                 inputblock.cn.push({
8366                     tag :'span',
8367                     cls : 'roo-input-after input-group-' +
8368                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8369                 });
8370             }
8371             
8372             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8373                 inputblock.cls += ' has-feedback';
8374                 inputblock.cn.push(feedback);
8375             }
8376         };
8377         
8378         if (align ==='left' && this.fieldLabel.length) {
8379             
8380             cfg.cn = [
8381                 {
8382                     tag : 'i',
8383                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8384                     tooltip : 'This field is required'
8385                 },
8386                 {
8387                     tag: 'label',
8388                     'for' :  id,
8389                     cls : 'control-label col-sm-' + this.labelWidth,
8390                     html : this.fieldLabel
8391
8392                 },
8393                 {
8394                     cls : "col-sm-" + (12 - this.labelWidth), 
8395                     cn: [
8396                         inputblock
8397                     ]
8398                 }
8399
8400             ];
8401             
8402             if(this.indicatorpos == 'right'){
8403                 cfg.cn = [
8404                     {
8405                         tag: 'label',
8406                         'for' :  id,
8407                         cls : 'control-label col-sm-' + this.labelWidth,
8408                         html : this.fieldLabel
8409
8410                     },
8411                     {
8412                         tag : 'i',
8413                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8414                         tooltip : 'This field is required'
8415                     },
8416                     {
8417                         cls : "col-sm-" + (12 - this.labelWidth), 
8418                         cn: [
8419                             inputblock
8420                         ]
8421                     }
8422
8423                 ];
8424             }
8425             
8426         } else if ( this.fieldLabel.length) {
8427                 
8428             cfg.cn = [
8429                 {
8430                     tag : 'i',
8431                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8432                     tooltip : 'This field is required'
8433                 },
8434                 {
8435                     tag: 'label',
8436                    //cls : 'input-group-addon',
8437                     html : this.fieldLabel
8438
8439                 },
8440
8441                inputblock
8442
8443            ];
8444            
8445            if(this.indicatorpos == 'right'){
8446                 
8447                 cfg.cn = [
8448                     {
8449                         tag: 'label',
8450                        //cls : 'input-group-addon',
8451                         html : this.fieldLabel
8452
8453                     },
8454                     {
8455                         tag : 'i',
8456                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8457                         tooltip : 'This field is required'
8458                     },
8459
8460                    inputblock
8461
8462                ];
8463
8464             }
8465
8466         } else {
8467             
8468             cfg.cn = [
8469
8470                     inputblock
8471
8472             ];
8473                 
8474                 
8475         };
8476         
8477         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8478            cfg.cls += ' navbar-form';
8479         }
8480         
8481         if (this.parentType === 'NavGroup') {
8482            cfg.cls += ' navbar-form';
8483            cfg.tag = 'li';
8484         }
8485         
8486         return cfg;
8487         
8488     },
8489     /**
8490      * return the real input element.
8491      */
8492     inputEl: function ()
8493     {
8494         return this.el.select('input.form-control',true).first();
8495     },
8496     
8497     tooltipEl : function()
8498     {
8499         return this.inputEl();
8500     },
8501     
8502     indicatorEl : function()
8503     {
8504         var indicator = this.el.select('i.roo-required-indicator',true).first();
8505         
8506         if(!indicator){
8507             return false;
8508         }
8509         
8510         return indicator;
8511         
8512     },
8513     
8514     setDisabled : function(v)
8515     {
8516         var i  = this.inputEl().dom;
8517         if (!v) {
8518             i.removeAttribute('disabled');
8519             return;
8520             
8521         }
8522         i.setAttribute('disabled','true');
8523     },
8524     initEvents : function()
8525     {
8526           
8527         this.inputEl().on("keydown" , this.fireKey,  this);
8528         this.inputEl().on("focus", this.onFocus,  this);
8529         this.inputEl().on("blur", this.onBlur,  this);
8530         
8531         this.inputEl().relayEvent('keyup', this);
8532         
8533         this.indicator = this.indicatorEl();
8534         
8535         if(this.indicator){
8536             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8537             this.indicator.hide();
8538         }
8539  
8540         // reference to original value for reset
8541         this.originalValue = this.getValue();
8542         //Roo.form.TextField.superclass.initEvents.call(this);
8543         if(this.validationEvent == 'keyup'){
8544             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8545             this.inputEl().on('keyup', this.filterValidation, this);
8546         }
8547         else if(this.validationEvent !== false){
8548             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8549         }
8550         
8551         if(this.selectOnFocus){
8552             this.on("focus", this.preFocus, this);
8553             
8554         }
8555         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8556             this.inputEl().on("keypress", this.filterKeys, this);
8557         } else {
8558             this.inputEl().relayEvent('keypress', this);
8559         }
8560        /* if(this.grow){
8561             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8562             this.el.on("click", this.autoSize,  this);
8563         }
8564         */
8565         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8566             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8567         }
8568         
8569         if (typeof(this.before) == 'object') {
8570             this.before.render(this.el.select('.roo-input-before',true).first());
8571         }
8572         if (typeof(this.after) == 'object') {
8573             this.after.render(this.el.select('.roo-input-after',true).first());
8574         }
8575         
8576         
8577     },
8578     filterValidation : function(e){
8579         if(!e.isNavKeyPress()){
8580             this.validationTask.delay(this.validationDelay);
8581         }
8582     },
8583      /**
8584      * Validates the field value
8585      * @return {Boolean} True if the value is valid, else false
8586      */
8587     validate : function(){
8588         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8589         if(this.disabled || this.validateValue(this.getRawValue())){
8590             this.markValid();
8591             return true;
8592         }
8593         
8594         this.markInvalid();
8595         return false;
8596     },
8597     
8598     
8599     /**
8600      * Validates a value according to the field's validation rules and marks the field as invalid
8601      * if the validation fails
8602      * @param {Mixed} value The value to validate
8603      * @return {Boolean} True if the value is valid, else false
8604      */
8605     validateValue : function(value){
8606         if(value.length < 1)  { // if it's blank
8607             if(this.allowBlank){
8608                 return true;
8609             }
8610             return false;
8611         }
8612         
8613         if(value.length < this.minLength){
8614             return false;
8615         }
8616         if(value.length > this.maxLength){
8617             return false;
8618         }
8619         if(this.vtype){
8620             var vt = Roo.form.VTypes;
8621             if(!vt[this.vtype](value, this)){
8622                 return false;
8623             }
8624         }
8625         if(typeof this.validator == "function"){
8626             var msg = this.validator(value);
8627             if(msg !== true){
8628                 return false;
8629             }
8630         }
8631         
8632         if(this.regex && !this.regex.test(value)){
8633             return false;
8634         }
8635         
8636         return true;
8637     },
8638
8639     
8640     
8641      // private
8642     fireKey : function(e){
8643         //Roo.log('field ' + e.getKey());
8644         if(e.isNavKeyPress()){
8645             this.fireEvent("specialkey", this, e);
8646         }
8647     },
8648     focus : function (selectText){
8649         if(this.rendered){
8650             this.inputEl().focus();
8651             if(selectText === true){
8652                 this.inputEl().dom.select();
8653             }
8654         }
8655         return this;
8656     } ,
8657     
8658     onFocus : function(){
8659         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8660            // this.el.addClass(this.focusClass);
8661         }
8662         if(!this.hasFocus){
8663             this.hasFocus = true;
8664             this.startValue = this.getValue();
8665             this.fireEvent("focus", this);
8666         }
8667     },
8668     
8669     beforeBlur : Roo.emptyFn,
8670
8671     
8672     // private
8673     onBlur : function(){
8674         this.beforeBlur();
8675         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8676             //this.el.removeClass(this.focusClass);
8677         }
8678         this.hasFocus = false;
8679         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8680             this.validate();
8681         }
8682         var v = this.getValue();
8683         if(String(v) !== String(this.startValue)){
8684             this.fireEvent('change', this, v, this.startValue);
8685         }
8686         this.fireEvent("blur", this);
8687     },
8688     
8689     /**
8690      * Resets the current field value to the originally loaded value and clears any validation messages
8691      */
8692     reset : function(){
8693         this.setValue(this.originalValue);
8694         this.validate();
8695     },
8696      /**
8697      * Returns the name of the field
8698      * @return {Mixed} name The name field
8699      */
8700     getName: function(){
8701         return this.name;
8702     },
8703      /**
8704      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8705      * @return {Mixed} value The field value
8706      */
8707     getValue : function(){
8708         
8709         var v = this.inputEl().getValue();
8710         
8711         return v;
8712     },
8713     /**
8714      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8715      * @return {Mixed} value The field value
8716      */
8717     getRawValue : function(){
8718         var v = this.inputEl().getValue();
8719         
8720         return v;
8721     },
8722     
8723     /**
8724      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8725      * @param {Mixed} value The value to set
8726      */
8727     setRawValue : function(v){
8728         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8729     },
8730     
8731     selectText : function(start, end){
8732         var v = this.getRawValue();
8733         if(v.length > 0){
8734             start = start === undefined ? 0 : start;
8735             end = end === undefined ? v.length : end;
8736             var d = this.inputEl().dom;
8737             if(d.setSelectionRange){
8738                 d.setSelectionRange(start, end);
8739             }else if(d.createTextRange){
8740                 var range = d.createTextRange();
8741                 range.moveStart("character", start);
8742                 range.moveEnd("character", v.length-end);
8743                 range.select();
8744             }
8745         }
8746     },
8747     
8748     /**
8749      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8750      * @param {Mixed} value The value to set
8751      */
8752     setValue : function(v){
8753         this.value = v;
8754         if(this.rendered){
8755             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8756             this.validate();
8757         }
8758     },
8759     
8760     /*
8761     processValue : function(value){
8762         if(this.stripCharsRe){
8763             var newValue = value.replace(this.stripCharsRe, '');
8764             if(newValue !== value){
8765                 this.setRawValue(newValue);
8766                 return newValue;
8767             }
8768         }
8769         return value;
8770     },
8771   */
8772     preFocus : function(){
8773         
8774         if(this.selectOnFocus){
8775             this.inputEl().dom.select();
8776         }
8777     },
8778     filterKeys : function(e){
8779         var k = e.getKey();
8780         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8781             return;
8782         }
8783         var c = e.getCharCode(), cc = String.fromCharCode(c);
8784         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8785             return;
8786         }
8787         if(!this.maskRe.test(cc)){
8788             e.stopEvent();
8789         }
8790     },
8791      /**
8792      * Clear any invalid styles/messages for this field
8793      */
8794     clearInvalid : function(){
8795         
8796         if(!this.el || this.preventMark){ // not rendered
8797             return;
8798         }
8799         
8800         if(this.indicator){
8801             this.indicator.hide();
8802         }
8803         
8804         this.el.removeClass(this.invalidClass);
8805         
8806         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8807             
8808             var feedback = this.el.select('.form-control-feedback', true).first();
8809             
8810             if(feedback){
8811                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8812             }
8813             
8814         }
8815         
8816         this.fireEvent('valid', this);
8817     },
8818     
8819      /**
8820      * Mark this field as valid
8821      */
8822     markValid : function()
8823     {
8824         if(!this.el  || this.preventMark){ // not rendered
8825             return;
8826         }
8827         
8828         this.el.removeClass([this.invalidClass, this.validClass]);
8829         
8830         var feedback = this.el.select('.form-control-feedback', true).first();
8831             
8832         if(feedback){
8833             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8834         }
8835
8836         if(this.disabled || this.allowBlank){
8837             return;
8838         }
8839         
8840         if(this.indicator){
8841             this.indicator.hide();
8842         }
8843         
8844         this.el.addClass(this.validClass);
8845         
8846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8847             
8848             var feedback = this.el.select('.form-control-feedback', true).first();
8849             
8850             if(feedback){
8851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8852                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8853             }
8854             
8855         }
8856         
8857         this.fireEvent('valid', this);
8858     },
8859     
8860      /**
8861      * Mark this field as invalid
8862      * @param {String} msg The validation message
8863      */
8864     markInvalid : function(msg)
8865     {
8866         if(!this.el  || this.preventMark){ // not rendered
8867             return;
8868         }
8869         
8870         this.el.removeClass([this.invalidClass, this.validClass]);
8871         
8872         var feedback = this.el.select('.form-control-feedback', true).first();
8873             
8874         if(feedback){
8875             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8876         }
8877
8878         if(this.disabled || this.allowBlank){
8879             return;
8880         }
8881         
8882         if(this.indicator){
8883             this.indicator.show();
8884         }
8885         
8886         this.el.addClass(this.invalidClass);
8887         
8888         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8889             
8890             var feedback = this.el.select('.form-control-feedback', true).first();
8891             
8892             if(feedback){
8893                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8894                 
8895                 if(this.getValue().length || this.forceFeedback){
8896                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8897                 }
8898                 
8899             }
8900             
8901         }
8902         
8903         this.fireEvent('invalid', this, msg);
8904     },
8905     // private
8906     SafariOnKeyDown : function(event)
8907     {
8908         // this is a workaround for a password hang bug on chrome/ webkit.
8909         
8910         var isSelectAll = false;
8911         
8912         if(this.inputEl().dom.selectionEnd > 0){
8913             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8914         }
8915         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8916             event.preventDefault();
8917             this.setValue('');
8918             return;
8919         }
8920         
8921         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8922             
8923             event.preventDefault();
8924             // this is very hacky as keydown always get's upper case.
8925             //
8926             var cc = String.fromCharCode(event.getCharCode());
8927             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8928             
8929         }
8930     },
8931     adjustWidth : function(tag, w){
8932         tag = tag.toLowerCase();
8933         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8934             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8935                 if(tag == 'input'){
8936                     return w + 2;
8937                 }
8938                 if(tag == 'textarea'){
8939                     return w-2;
8940                 }
8941             }else if(Roo.isOpera){
8942                 if(tag == 'input'){
8943                     return w + 2;
8944                 }
8945                 if(tag == 'textarea'){
8946                     return w-2;
8947                 }
8948             }
8949         }
8950         return w;
8951     }
8952     
8953 });
8954
8955  
8956 /*
8957  * - LGPL
8958  *
8959  * Input
8960  * 
8961  */
8962
8963 /**
8964  * @class Roo.bootstrap.TextArea
8965  * @extends Roo.bootstrap.Input
8966  * Bootstrap TextArea class
8967  * @cfg {Number} cols Specifies the visible width of a text area
8968  * @cfg {Number} rows Specifies the visible number of lines in a text area
8969  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8970  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8971  * @cfg {string} html text
8972  * 
8973  * @constructor
8974  * Create a new TextArea
8975  * @param {Object} config The config object
8976  */
8977
8978 Roo.bootstrap.TextArea = function(config){
8979     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8980    
8981 };
8982
8983 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8984      
8985     cols : false,
8986     rows : 5,
8987     readOnly : false,
8988     warp : 'soft',
8989     resize : false,
8990     value: false,
8991     html: false,
8992     
8993     getAutoCreate : function(){
8994         
8995         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8996         
8997         var id = Roo.id();
8998         
8999         var cfg = {};
9000         
9001         var input =  {
9002             tag: 'textarea',
9003             id : id,
9004             warp : this.warp,
9005             rows : this.rows,
9006             value : this.value || '',
9007             html: this.html || '',
9008             cls : 'form-control',
9009             placeholder : this.placeholder || '' 
9010             
9011         };
9012         
9013         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9014             input.maxLength = this.maxLength;
9015         }
9016         
9017         if(this.resize){
9018             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9019         }
9020         
9021         if(this.cols){
9022             input.cols = this.cols;
9023         }
9024         
9025         if (this.readOnly) {
9026             input.readonly = true;
9027         }
9028         
9029         if (this.name) {
9030             input.name = this.name;
9031         }
9032         
9033         if (this.size) {
9034             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9035         }
9036         
9037         var settings=this;
9038         ['xs','sm','md','lg'].map(function(size){
9039             if (settings[size]) {
9040                 cfg.cls += ' col-' + size + '-' + settings[size];
9041             }
9042         });
9043         
9044         var inputblock = input;
9045         
9046         if(this.hasFeedback && !this.allowBlank){
9047             
9048             var feedback = {
9049                 tag: 'span',
9050                 cls: 'glyphicon form-control-feedback'
9051             };
9052
9053             inputblock = {
9054                 cls : 'has-feedback',
9055                 cn :  [
9056                     input,
9057                     feedback
9058                 ] 
9059             };  
9060         }
9061         
9062         
9063         if (this.before || this.after) {
9064             
9065             inputblock = {
9066                 cls : 'input-group',
9067                 cn :  [] 
9068             };
9069             if (this.before) {
9070                 inputblock.cn.push({
9071                     tag :'span',
9072                     cls : 'input-group-addon',
9073                     html : this.before
9074                 });
9075             }
9076             
9077             inputblock.cn.push(input);
9078             
9079             if(this.hasFeedback && !this.allowBlank){
9080                 inputblock.cls += ' has-feedback';
9081                 inputblock.cn.push(feedback);
9082             }
9083             
9084             if (this.after) {
9085                 inputblock.cn.push({
9086                     tag :'span',
9087                     cls : 'input-group-addon',
9088                     html : this.after
9089                 });
9090             }
9091             
9092         }
9093         
9094         if (align ==='left' && this.fieldLabel.length) {
9095 //                Roo.log("left and has label");
9096                 cfg.cn = [
9097                     
9098                     {
9099                         tag: 'label',
9100                         'for' :  id,
9101                         cls : 'control-label col-sm-' + this.labelWidth,
9102                         html : this.fieldLabel
9103                         
9104                     },
9105                     {
9106                         cls : "col-sm-" + (12 - this.labelWidth), 
9107                         cn: [
9108                             inputblock
9109                         ]
9110                     }
9111                     
9112                 ];
9113         } else if ( this.fieldLabel.length) {
9114 //                Roo.log(" label");
9115                  cfg.cn = [
9116                    
9117                     {
9118                         tag: 'label',
9119                         //cls : 'input-group-addon',
9120                         html : this.fieldLabel
9121                         
9122                     },
9123                     
9124                     inputblock
9125                     
9126                 ];
9127
9128         } else {
9129             
9130 //                   Roo.log(" no label && no align");
9131                 cfg.cn = [
9132                     
9133                         inputblock
9134                     
9135                 ];
9136                 
9137                 
9138         }
9139         
9140         if (this.disabled) {
9141             input.disabled=true;
9142         }
9143         
9144         return cfg;
9145         
9146     },
9147     /**
9148      * return the real textarea element.
9149      */
9150     inputEl: function ()
9151     {
9152         return this.el.select('textarea.form-control',true).first();
9153     },
9154     
9155     /**
9156      * Clear any invalid styles/messages for this field
9157      */
9158     clearInvalid : function()
9159     {
9160         
9161         if(!this.el || this.preventMark){ // not rendered
9162             return;
9163         }
9164         
9165         var label = this.el.select('label', true).first();
9166         var icon = this.el.select('i.fa-star', true).first();
9167         
9168         if(label && icon){
9169             icon.remove();
9170         }
9171         
9172         this.el.removeClass(this.invalidClass);
9173         
9174         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9175             
9176             var feedback = this.el.select('.form-control-feedback', true).first();
9177             
9178             if(feedback){
9179                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9180             }
9181             
9182         }
9183         
9184         this.fireEvent('valid', this);
9185     },
9186     
9187      /**
9188      * Mark this field as valid
9189      */
9190     markValid : function()
9191     {
9192         if(!this.el  || this.preventMark){ // not rendered
9193             return;
9194         }
9195         
9196         this.el.removeClass([this.invalidClass, this.validClass]);
9197         
9198         var feedback = this.el.select('.form-control-feedback', true).first();
9199             
9200         if(feedback){
9201             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9202         }
9203
9204         if(this.disabled || this.allowBlank){
9205             return;
9206         }
9207         
9208         var label = this.el.select('label', true).first();
9209         var icon = this.el.select('i.fa-star', true).first();
9210         
9211         if(label && icon){
9212             icon.remove();
9213         }
9214         
9215         this.el.addClass(this.validClass);
9216         
9217         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9218             
9219             var feedback = this.el.select('.form-control-feedback', true).first();
9220             
9221             if(feedback){
9222                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9223                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9224             }
9225             
9226         }
9227         
9228         this.fireEvent('valid', this);
9229     },
9230     
9231      /**
9232      * Mark this field as invalid
9233      * @param {String} msg The validation message
9234      */
9235     markInvalid : function(msg)
9236     {
9237         if(!this.el  || this.preventMark){ // not rendered
9238             return;
9239         }
9240         
9241         this.el.removeClass([this.invalidClass, this.validClass]);
9242         
9243         var feedback = this.el.select('.form-control-feedback', true).first();
9244             
9245         if(feedback){
9246             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9247         }
9248
9249         if(this.disabled || this.allowBlank){
9250             return;
9251         }
9252         
9253         var label = this.el.select('label', true).first();
9254         var icon = this.el.select('i.fa-star', true).first();
9255         
9256         if(!this.getValue().length && label && !icon){
9257             this.el.createChild({
9258                 tag : 'i',
9259                 cls : 'text-danger fa fa-lg fa-star',
9260                 tooltip : 'This field is required',
9261                 style : 'margin-right:5px;'
9262             }, label, true);
9263         }
9264
9265         this.el.addClass(this.invalidClass);
9266         
9267         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9268             
9269             var feedback = this.el.select('.form-control-feedback', true).first();
9270             
9271             if(feedback){
9272                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9273                 
9274                 if(this.getValue().length || this.forceFeedback){
9275                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9276                 }
9277                 
9278             }
9279             
9280         }
9281         
9282         this.fireEvent('invalid', this, msg);
9283     }
9284 });
9285
9286  
9287 /*
9288  * - LGPL
9289  *
9290  * trigger field - base class for combo..
9291  * 
9292  */
9293  
9294 /**
9295  * @class Roo.bootstrap.TriggerField
9296  * @extends Roo.bootstrap.Input
9297  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9298  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9299  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9300  * for which you can provide a custom implementation.  For example:
9301  * <pre><code>
9302 var trigger = new Roo.bootstrap.TriggerField();
9303 trigger.onTriggerClick = myTriggerFn;
9304 trigger.applyTo('my-field');
9305 </code></pre>
9306  *
9307  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9308  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9309  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9310  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9311  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9312
9313  * @constructor
9314  * Create a new TriggerField.
9315  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9316  * to the base TextField)
9317  */
9318 Roo.bootstrap.TriggerField = function(config){
9319     this.mimicing = false;
9320     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9321 };
9322
9323 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9324     /**
9325      * @cfg {String} triggerClass A CSS class to apply to the trigger
9326      */
9327      /**
9328      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9329      */
9330     hideTrigger:false,
9331
9332     /**
9333      * @cfg {Boolean} removable (true|false) special filter default false
9334      */
9335     removable : false,
9336     
9337     /** @cfg {Boolean} grow @hide */
9338     /** @cfg {Number} growMin @hide */
9339     /** @cfg {Number} growMax @hide */
9340
9341     /**
9342      * @hide 
9343      * @method
9344      */
9345     autoSize: Roo.emptyFn,
9346     // private
9347     monitorTab : true,
9348     // private
9349     deferHeight : true,
9350
9351     
9352     actionMode : 'wrap',
9353     
9354     caret : false,
9355     
9356     
9357     getAutoCreate : function(){
9358        
9359         var align = this.labelAlign || this.parentLabelAlign();
9360         
9361         var id = Roo.id();
9362         
9363         var cfg = {
9364             cls: 'form-group' //input-group
9365         };
9366         
9367         
9368         var input =  {
9369             tag: 'input',
9370             id : id,
9371             type : this.inputType,
9372             cls : 'form-control',
9373             autocomplete: 'new-password',
9374             placeholder : this.placeholder || '' 
9375             
9376         };
9377         if (this.name) {
9378             input.name = this.name;
9379         }
9380         if (this.size) {
9381             input.cls += ' input-' + this.size;
9382         }
9383         
9384         if (this.disabled) {
9385             input.disabled=true;
9386         }
9387         
9388         var inputblock = input;
9389         
9390         if(this.hasFeedback && !this.allowBlank){
9391             
9392             var feedback = {
9393                 tag: 'span',
9394                 cls: 'glyphicon form-control-feedback'
9395             };
9396             
9397             if(this.removable && !this.editable && !this.tickable){
9398                 inputblock = {
9399                     cls : 'has-feedback',
9400                     cn :  [
9401                         inputblock,
9402                         {
9403                             tag: 'button',
9404                             html : 'x',
9405                             cls : 'roo-combo-removable-btn close'
9406                         },
9407                         feedback
9408                     ] 
9409                 };
9410             } else {
9411                 inputblock = {
9412                     cls : 'has-feedback',
9413                     cn :  [
9414                         inputblock,
9415                         feedback
9416                     ] 
9417                 };
9418             }
9419
9420         } else {
9421             if(this.removable && !this.editable && !this.tickable){
9422                 inputblock = {
9423                     cls : 'roo-removable',
9424                     cn :  [
9425                         inputblock,
9426                         {
9427                             tag: 'button',
9428                             html : 'x',
9429                             cls : 'roo-combo-removable-btn close'
9430                         }
9431                     ] 
9432                 };
9433             }
9434         }
9435         
9436         if (this.before || this.after) {
9437             
9438             inputblock = {
9439                 cls : 'input-group',
9440                 cn :  [] 
9441             };
9442             if (this.before) {
9443                 inputblock.cn.push({
9444                     tag :'span',
9445                     cls : 'input-group-addon',
9446                     html : this.before
9447                 });
9448             }
9449             
9450             inputblock.cn.push(input);
9451             
9452             if(this.hasFeedback && !this.allowBlank){
9453                 inputblock.cls += ' has-feedback';
9454                 inputblock.cn.push(feedback);
9455             }
9456             
9457             if (this.after) {
9458                 inputblock.cn.push({
9459                     tag :'span',
9460                     cls : 'input-group-addon',
9461                     html : this.after
9462                 });
9463             }
9464             
9465         };
9466         
9467         var box = {
9468             tag: 'div',
9469             cn: [
9470                 {
9471                     tag: 'input',
9472                     type : 'hidden',
9473                     cls: 'form-hidden-field'
9474                 },
9475                 inputblock
9476             ]
9477             
9478         };
9479         
9480         if(this.multiple){
9481             box = {
9482                 tag: 'div',
9483                 cn: [
9484                     {
9485                         tag: 'input',
9486                         type : 'hidden',
9487                         cls: 'form-hidden-field'
9488                     },
9489                     {
9490                         tag: 'ul',
9491                         cls: 'roo-select2-choices',
9492                         cn:[
9493                             {
9494                                 tag: 'li',
9495                                 cls: 'roo-select2-search-field',
9496                                 cn: [
9497
9498                                     inputblock
9499                                 ]
9500                             }
9501                         ]
9502                     }
9503                 ]
9504             }
9505         };
9506         
9507         var combobox = {
9508             cls: 'roo-select2-container input-group',
9509             cn: [
9510                 box
9511 //                {
9512 //                    tag: 'ul',
9513 //                    cls: 'typeahead typeahead-long dropdown-menu',
9514 //                    style: 'display:none'
9515 //                }
9516             ]
9517         };
9518         
9519         if(!this.multiple && this.showToggleBtn){
9520             
9521             var caret = {
9522                         tag: 'span',
9523                         cls: 'caret'
9524              };
9525             if (this.caret != false) {
9526                 caret = {
9527                      tag: 'i',
9528                      cls: 'fa fa-' + this.caret
9529                 };
9530                 
9531             }
9532             
9533             combobox.cn.push({
9534                 tag :'span',
9535                 cls : 'input-group-addon btn dropdown-toggle',
9536                 cn : [
9537                     caret,
9538                     {
9539                         tag: 'span',
9540                         cls: 'combobox-clear',
9541                         cn  : [
9542                             {
9543                                 tag : 'i',
9544                                 cls: 'icon-remove'
9545                             }
9546                         ]
9547                     }
9548                 ]
9549
9550             })
9551         }
9552         
9553         if(this.multiple){
9554             combobox.cls += ' roo-select2-container-multi';
9555         }
9556         
9557         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9558             
9559 //                Roo.log("left and has label");
9560             cfg.cn = [
9561                 {
9562                     tag : 'i',
9563                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9564                     tooltip : 'This field is required'
9565                 },
9566                 {
9567                     tag: 'label',
9568                     'for' :  id,
9569                     cls : 'control-label col-sm-' + this.labelWidth,
9570                     html : this.fieldLabel
9571
9572                 },
9573                 {
9574                     cls : "col-sm-" + (12 - this.labelWidth), 
9575                     cn: [
9576                         combobox
9577                     ]
9578                 }
9579
9580             ];
9581             
9582             if(this.indicatorpos == 'right'){
9583                 cfg.cn = [
9584                     {
9585                         tag: 'label',
9586                         'for' :  id,
9587                         cls : 'control-label col-sm-' + this.labelWidth,
9588                         html : this.fieldLabel
9589
9590                     },
9591                     {
9592                         tag : 'i',
9593                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9594                         tooltip : 'This field is required'
9595                     },
9596                     {
9597                         cls : "col-sm-" + (12 - this.labelWidth), 
9598                         cn: [
9599                             combobox
9600                         ]
9601                     }
9602
9603                 ];
9604             }
9605             
9606         } else if ( this.fieldLabel.length) {
9607 //                Roo.log(" label");
9608             cfg.cn = [
9609                 {
9610                    tag : 'i',
9611                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9612                    tooltip : 'This field is required'
9613                },
9614                {
9615                    tag: 'label',
9616                    //cls : 'input-group-addon',
9617                    html : this.fieldLabel
9618
9619                },
9620
9621                combobox
9622
9623             ];
9624             
9625             if(this.indicatorpos == 'right'){
9626                 
9627                 cfg.cn = [
9628                     {
9629                        tag: 'label',
9630                        //cls : 'input-group-addon',
9631                        html : this.fieldLabel
9632
9633                     },
9634                     {
9635                        tag : 'i',
9636                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9637                        tooltip : 'This field is required'
9638                     },
9639                     
9640                     combobox
9641
9642                 ];
9643
9644             }
9645
9646         } else {
9647             
9648 //                Roo.log(" no label && no align");
9649                 cfg = combobox
9650                      
9651                 
9652         }
9653          
9654         var settings=this;
9655         ['xs','sm','md','lg'].map(function(size){
9656             if (settings[size]) {
9657                 cfg.cls += ' col-' + size + '-' + settings[size];
9658             }
9659         });
9660         
9661         return cfg;
9662         
9663     },
9664     
9665     
9666     
9667     // private
9668     onResize : function(w, h){
9669 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9670 //        if(typeof w == 'number'){
9671 //            var x = w - this.trigger.getWidth();
9672 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9673 //            this.trigger.setStyle('left', x+'px');
9674 //        }
9675     },
9676
9677     // private
9678     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9679
9680     // private
9681     getResizeEl : function(){
9682         return this.inputEl();
9683     },
9684
9685     // private
9686     getPositionEl : function(){
9687         return this.inputEl();
9688     },
9689
9690     // private
9691     alignErrorIcon : function(){
9692         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9693     },
9694
9695     // private
9696     initEvents : function(){
9697         
9698         this.createList();
9699         
9700         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9701         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9702         if(!this.multiple && this.showToggleBtn){
9703             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9704             if(this.hideTrigger){
9705                 this.trigger.setDisplayed(false);
9706             }
9707             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9708         }
9709         
9710         if(this.multiple){
9711             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9712         }
9713         
9714         if(this.removable && !this.editable && !this.tickable){
9715             var close = this.closeTriggerEl();
9716             
9717             if(close){
9718                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9719                 close.on('click', this.removeBtnClick, this, close);
9720             }
9721         }
9722         
9723         //this.trigger.addClassOnOver('x-form-trigger-over');
9724         //this.trigger.addClassOnClick('x-form-trigger-click');
9725         
9726         //if(!this.width){
9727         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9728         //}
9729     },
9730     
9731     closeTriggerEl : function()
9732     {
9733         var close = this.el.select('.roo-combo-removable-btn', true).first();
9734         return close ? close : false;
9735     },
9736     
9737     removeBtnClick : function(e, h, el)
9738     {
9739         e.preventDefault();
9740         
9741         if(this.fireEvent("remove", this) !== false){
9742             this.reset();
9743             this.fireEvent("afterremove", this)
9744         }
9745     },
9746     
9747     createList : function()
9748     {
9749         this.list = Roo.get(document.body).createChild({
9750             tag: 'ul',
9751             cls: 'typeahead typeahead-long dropdown-menu',
9752             style: 'display:none'
9753         });
9754         
9755         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9756         
9757     },
9758
9759     // private
9760     initTrigger : function(){
9761        
9762     },
9763
9764     // private
9765     onDestroy : function(){
9766         if(this.trigger){
9767             this.trigger.removeAllListeners();
9768           //  this.trigger.remove();
9769         }
9770         //if(this.wrap){
9771         //    this.wrap.remove();
9772         //}
9773         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9774     },
9775
9776     // private
9777     onFocus : function(){
9778         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9779         /*
9780         if(!this.mimicing){
9781             this.wrap.addClass('x-trigger-wrap-focus');
9782             this.mimicing = true;
9783             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9784             if(this.monitorTab){
9785                 this.el.on("keydown", this.checkTab, this);
9786             }
9787         }
9788         */
9789     },
9790
9791     // private
9792     checkTab : function(e){
9793         if(e.getKey() == e.TAB){
9794             this.triggerBlur();
9795         }
9796     },
9797
9798     // private
9799     onBlur : function(){
9800         // do nothing
9801     },
9802
9803     // private
9804     mimicBlur : function(e, t){
9805         /*
9806         if(!this.wrap.contains(t) && this.validateBlur()){
9807             this.triggerBlur();
9808         }
9809         */
9810     },
9811
9812     // private
9813     triggerBlur : function(){
9814         this.mimicing = false;
9815         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9816         if(this.monitorTab){
9817             this.el.un("keydown", this.checkTab, this);
9818         }
9819         //this.wrap.removeClass('x-trigger-wrap-focus');
9820         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9821     },
9822
9823     // private
9824     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9825     validateBlur : function(e, t){
9826         return true;
9827     },
9828
9829     // private
9830     onDisable : function(){
9831         this.inputEl().dom.disabled = true;
9832         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9833         //if(this.wrap){
9834         //    this.wrap.addClass('x-item-disabled');
9835         //}
9836     },
9837
9838     // private
9839     onEnable : function(){
9840         this.inputEl().dom.disabled = false;
9841         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9842         //if(this.wrap){
9843         //    this.el.removeClass('x-item-disabled');
9844         //}
9845     },
9846
9847     // private
9848     onShow : function(){
9849         var ae = this.getActionEl();
9850         
9851         if(ae){
9852             ae.dom.style.display = '';
9853             ae.dom.style.visibility = 'visible';
9854         }
9855     },
9856
9857     // private
9858     
9859     onHide : function(){
9860         var ae = this.getActionEl();
9861         ae.dom.style.display = 'none';
9862     },
9863
9864     /**
9865      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9866      * by an implementing function.
9867      * @method
9868      * @param {EventObject} e
9869      */
9870     onTriggerClick : Roo.emptyFn
9871 });
9872  /*
9873  * Based on:
9874  * Ext JS Library 1.1.1
9875  * Copyright(c) 2006-2007, Ext JS, LLC.
9876  *
9877  * Originally Released Under LGPL - original licence link has changed is not relivant.
9878  *
9879  * Fork - LGPL
9880  * <script type="text/javascript">
9881  */
9882
9883
9884 /**
9885  * @class Roo.data.SortTypes
9886  * @singleton
9887  * Defines the default sorting (casting?) comparison functions used when sorting data.
9888  */
9889 Roo.data.SortTypes = {
9890     /**
9891      * Default sort that does nothing
9892      * @param {Mixed} s The value being converted
9893      * @return {Mixed} The comparison value
9894      */
9895     none : function(s){
9896         return s;
9897     },
9898     
9899     /**
9900      * The regular expression used to strip tags
9901      * @type {RegExp}
9902      * @property
9903      */
9904     stripTagsRE : /<\/?[^>]+>/gi,
9905     
9906     /**
9907      * Strips all HTML tags to sort on text only
9908      * @param {Mixed} s The value being converted
9909      * @return {String} The comparison value
9910      */
9911     asText : function(s){
9912         return String(s).replace(this.stripTagsRE, "");
9913     },
9914     
9915     /**
9916      * Strips all HTML tags to sort on text only - Case insensitive
9917      * @param {Mixed} s The value being converted
9918      * @return {String} The comparison value
9919      */
9920     asUCText : function(s){
9921         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9922     },
9923     
9924     /**
9925      * Case insensitive string
9926      * @param {Mixed} s The value being converted
9927      * @return {String} The comparison value
9928      */
9929     asUCString : function(s) {
9930         return String(s).toUpperCase();
9931     },
9932     
9933     /**
9934      * Date sorting
9935      * @param {Mixed} s The value being converted
9936      * @return {Number} The comparison value
9937      */
9938     asDate : function(s) {
9939         if(!s){
9940             return 0;
9941         }
9942         if(s instanceof Date){
9943             return s.getTime();
9944         }
9945         return Date.parse(String(s));
9946     },
9947     
9948     /**
9949      * Float sorting
9950      * @param {Mixed} s The value being converted
9951      * @return {Float} The comparison value
9952      */
9953     asFloat : function(s) {
9954         var val = parseFloat(String(s).replace(/,/g, ""));
9955         if(isNaN(val)) {
9956             val = 0;
9957         }
9958         return val;
9959     },
9960     
9961     /**
9962      * Integer sorting
9963      * @param {Mixed} s The value being converted
9964      * @return {Number} The comparison value
9965      */
9966     asInt : function(s) {
9967         var val = parseInt(String(s).replace(/,/g, ""));
9968         if(isNaN(val)) {
9969             val = 0;
9970         }
9971         return val;
9972     }
9973 };/*
9974  * Based on:
9975  * Ext JS Library 1.1.1
9976  * Copyright(c) 2006-2007, Ext JS, LLC.
9977  *
9978  * Originally Released Under LGPL - original licence link has changed is not relivant.
9979  *
9980  * Fork - LGPL
9981  * <script type="text/javascript">
9982  */
9983
9984 /**
9985 * @class Roo.data.Record
9986  * Instances of this class encapsulate both record <em>definition</em> information, and record
9987  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9988  * to access Records cached in an {@link Roo.data.Store} object.<br>
9989  * <p>
9990  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9991  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9992  * objects.<br>
9993  * <p>
9994  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9995  * @constructor
9996  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9997  * {@link #create}. The parameters are the same.
9998  * @param {Array} data An associative Array of data values keyed by the field name.
9999  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10000  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10001  * not specified an integer id is generated.
10002  */
10003 Roo.data.Record = function(data, id){
10004     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10005     this.data = data;
10006 };
10007
10008 /**
10009  * Generate a constructor for a specific record layout.
10010  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10011  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10012  * Each field definition object may contain the following properties: <ul>
10013  * <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,
10014  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10015  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10016  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10017  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10018  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10019  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10020  * this may be omitted.</p></li>
10021  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10022  * <ul><li>auto (Default, implies no conversion)</li>
10023  * <li>string</li>
10024  * <li>int</li>
10025  * <li>float</li>
10026  * <li>boolean</li>
10027  * <li>date</li></ul></p></li>
10028  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10029  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10030  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10031  * by the Reader into an object that will be stored in the Record. It is passed the
10032  * following parameters:<ul>
10033  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10034  * </ul></p></li>
10035  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10036  * </ul>
10037  * <br>usage:<br><pre><code>
10038 var TopicRecord = Roo.data.Record.create(
10039     {name: 'title', mapping: 'topic_title'},
10040     {name: 'author', mapping: 'username'},
10041     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10042     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10043     {name: 'lastPoster', mapping: 'user2'},
10044     {name: 'excerpt', mapping: 'post_text'}
10045 );
10046
10047 var myNewRecord = new TopicRecord({
10048     title: 'Do my job please',
10049     author: 'noobie',
10050     totalPosts: 1,
10051     lastPost: new Date(),
10052     lastPoster: 'Animal',
10053     excerpt: 'No way dude!'
10054 });
10055 myStore.add(myNewRecord);
10056 </code></pre>
10057  * @method create
10058  * @static
10059  */
10060 Roo.data.Record.create = function(o){
10061     var f = function(){
10062         f.superclass.constructor.apply(this, arguments);
10063     };
10064     Roo.extend(f, Roo.data.Record);
10065     var p = f.prototype;
10066     p.fields = new Roo.util.MixedCollection(false, function(field){
10067         return field.name;
10068     });
10069     for(var i = 0, len = o.length; i < len; i++){
10070         p.fields.add(new Roo.data.Field(o[i]));
10071     }
10072     f.getField = function(name){
10073         return p.fields.get(name);  
10074     };
10075     return f;
10076 };
10077
10078 Roo.data.Record.AUTO_ID = 1000;
10079 Roo.data.Record.EDIT = 'edit';
10080 Roo.data.Record.REJECT = 'reject';
10081 Roo.data.Record.COMMIT = 'commit';
10082
10083 Roo.data.Record.prototype = {
10084     /**
10085      * Readonly flag - true if this record has been modified.
10086      * @type Boolean
10087      */
10088     dirty : false,
10089     editing : false,
10090     error: null,
10091     modified: null,
10092
10093     // private
10094     join : function(store){
10095         this.store = store;
10096     },
10097
10098     /**
10099      * Set the named field to the specified value.
10100      * @param {String} name The name of the field to set.
10101      * @param {Object} value The value to set the field to.
10102      */
10103     set : function(name, value){
10104         if(this.data[name] == value){
10105             return;
10106         }
10107         this.dirty = true;
10108         if(!this.modified){
10109             this.modified = {};
10110         }
10111         if(typeof this.modified[name] == 'undefined'){
10112             this.modified[name] = this.data[name];
10113         }
10114         this.data[name] = value;
10115         if(!this.editing && this.store){
10116             this.store.afterEdit(this);
10117         }       
10118     },
10119
10120     /**
10121      * Get the value of the named field.
10122      * @param {String} name The name of the field to get the value of.
10123      * @return {Object} The value of the field.
10124      */
10125     get : function(name){
10126         return this.data[name]; 
10127     },
10128
10129     // private
10130     beginEdit : function(){
10131         this.editing = true;
10132         this.modified = {}; 
10133     },
10134
10135     // private
10136     cancelEdit : function(){
10137         this.editing = false;
10138         delete this.modified;
10139     },
10140
10141     // private
10142     endEdit : function(){
10143         this.editing = false;
10144         if(this.dirty && this.store){
10145             this.store.afterEdit(this);
10146         }
10147     },
10148
10149     /**
10150      * Usually called by the {@link Roo.data.Store} which owns the Record.
10151      * Rejects all changes made to the Record since either creation, or the last commit operation.
10152      * Modified fields are reverted to their original values.
10153      * <p>
10154      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10155      * of reject operations.
10156      */
10157     reject : function(){
10158         var m = this.modified;
10159         for(var n in m){
10160             if(typeof m[n] != "function"){
10161                 this.data[n] = m[n];
10162             }
10163         }
10164         this.dirty = false;
10165         delete this.modified;
10166         this.editing = false;
10167         if(this.store){
10168             this.store.afterReject(this);
10169         }
10170     },
10171
10172     /**
10173      * Usually called by the {@link Roo.data.Store} which owns the Record.
10174      * Commits all changes made to the Record since either creation, or the last commit operation.
10175      * <p>
10176      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10177      * of commit operations.
10178      */
10179     commit : function(){
10180         this.dirty = false;
10181         delete this.modified;
10182         this.editing = false;
10183         if(this.store){
10184             this.store.afterCommit(this);
10185         }
10186     },
10187
10188     // private
10189     hasError : function(){
10190         return this.error != null;
10191     },
10192
10193     // private
10194     clearError : function(){
10195         this.error = null;
10196     },
10197
10198     /**
10199      * Creates a copy of this record.
10200      * @param {String} id (optional) A new record id if you don't want to use this record's id
10201      * @return {Record}
10202      */
10203     copy : function(newId) {
10204         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10205     }
10206 };/*
10207  * Based on:
10208  * Ext JS Library 1.1.1
10209  * Copyright(c) 2006-2007, Ext JS, LLC.
10210  *
10211  * Originally Released Under LGPL - original licence link has changed is not relivant.
10212  *
10213  * Fork - LGPL
10214  * <script type="text/javascript">
10215  */
10216
10217
10218
10219 /**
10220  * @class Roo.data.Store
10221  * @extends Roo.util.Observable
10222  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10223  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10224  * <p>
10225  * 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
10226  * has no knowledge of the format of the data returned by the Proxy.<br>
10227  * <p>
10228  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10229  * instances from the data object. These records are cached and made available through accessor functions.
10230  * @constructor
10231  * Creates a new Store.
10232  * @param {Object} config A config object containing the objects needed for the Store to access data,
10233  * and read the data into Records.
10234  */
10235 Roo.data.Store = function(config){
10236     this.data = new Roo.util.MixedCollection(false);
10237     this.data.getKey = function(o){
10238         return o.id;
10239     };
10240     this.baseParams = {};
10241     // private
10242     this.paramNames = {
10243         "start" : "start",
10244         "limit" : "limit",
10245         "sort" : "sort",
10246         "dir" : "dir",
10247         "multisort" : "_multisort"
10248     };
10249
10250     if(config && config.data){
10251         this.inlineData = config.data;
10252         delete config.data;
10253     }
10254
10255     Roo.apply(this, config);
10256     
10257     if(this.reader){ // reader passed
10258         this.reader = Roo.factory(this.reader, Roo.data);
10259         this.reader.xmodule = this.xmodule || false;
10260         if(!this.recordType){
10261             this.recordType = this.reader.recordType;
10262         }
10263         if(this.reader.onMetaChange){
10264             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10265         }
10266     }
10267
10268     if(this.recordType){
10269         this.fields = this.recordType.prototype.fields;
10270     }
10271     this.modified = [];
10272
10273     this.addEvents({
10274         /**
10275          * @event datachanged
10276          * Fires when the data cache has changed, and a widget which is using this Store
10277          * as a Record cache should refresh its view.
10278          * @param {Store} this
10279          */
10280         datachanged : true,
10281         /**
10282          * @event metachange
10283          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10284          * @param {Store} this
10285          * @param {Object} meta The JSON metadata
10286          */
10287         metachange : true,
10288         /**
10289          * @event add
10290          * Fires when Records have been added to the Store
10291          * @param {Store} this
10292          * @param {Roo.data.Record[]} records The array of Records added
10293          * @param {Number} index The index at which the record(s) were added
10294          */
10295         add : true,
10296         /**
10297          * @event remove
10298          * Fires when a Record has been removed from the Store
10299          * @param {Store} this
10300          * @param {Roo.data.Record} record The Record that was removed
10301          * @param {Number} index The index at which the record was removed
10302          */
10303         remove : true,
10304         /**
10305          * @event update
10306          * Fires when a Record has been updated
10307          * @param {Store} this
10308          * @param {Roo.data.Record} record The Record that was updated
10309          * @param {String} operation The update operation being performed.  Value may be one of:
10310          * <pre><code>
10311  Roo.data.Record.EDIT
10312  Roo.data.Record.REJECT
10313  Roo.data.Record.COMMIT
10314          * </code></pre>
10315          */
10316         update : true,
10317         /**
10318          * @event clear
10319          * Fires when the data cache has been cleared.
10320          * @param {Store} this
10321          */
10322         clear : true,
10323         /**
10324          * @event beforeload
10325          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10326          * the load action will be canceled.
10327          * @param {Store} this
10328          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10329          */
10330         beforeload : true,
10331         /**
10332          * @event beforeloadadd
10333          * Fires after a new set of Records has been loaded.
10334          * @param {Store} this
10335          * @param {Roo.data.Record[]} records The Records that were loaded
10336          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10337          */
10338         beforeloadadd : true,
10339         /**
10340          * @event load
10341          * Fires after a new set of Records has been loaded, before they are added to the store.
10342          * @param {Store} this
10343          * @param {Roo.data.Record[]} records The Records that were loaded
10344          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10345          * @params {Object} return from reader
10346          */
10347         load : true,
10348         /**
10349          * @event loadexception
10350          * Fires if an exception occurs in the Proxy during loading.
10351          * Called with the signature of the Proxy's "loadexception" event.
10352          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10353          * 
10354          * @param {Proxy} 
10355          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10356          * @param {Object} load options 
10357          * @param {Object} jsonData from your request (normally this contains the Exception)
10358          */
10359         loadexception : true
10360     });
10361     
10362     if(this.proxy){
10363         this.proxy = Roo.factory(this.proxy, Roo.data);
10364         this.proxy.xmodule = this.xmodule || false;
10365         this.relayEvents(this.proxy,  ["loadexception"]);
10366     }
10367     this.sortToggle = {};
10368     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10369
10370     Roo.data.Store.superclass.constructor.call(this);
10371
10372     if(this.inlineData){
10373         this.loadData(this.inlineData);
10374         delete this.inlineData;
10375     }
10376 };
10377
10378 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10379      /**
10380     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10381     * without a remote query - used by combo/forms at present.
10382     */
10383     
10384     /**
10385     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10386     */
10387     /**
10388     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10389     */
10390     /**
10391     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10392     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10393     */
10394     /**
10395     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10396     * on any HTTP request
10397     */
10398     /**
10399     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10400     */
10401     /**
10402     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10403     */
10404     multiSort: false,
10405     /**
10406     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10407     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10408     */
10409     remoteSort : false,
10410
10411     /**
10412     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10413      * loaded or when a record is removed. (defaults to false).
10414     */
10415     pruneModifiedRecords : false,
10416
10417     // private
10418     lastOptions : null,
10419
10420     /**
10421      * Add Records to the Store and fires the add event.
10422      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10423      */
10424     add : function(records){
10425         records = [].concat(records);
10426         for(var i = 0, len = records.length; i < len; i++){
10427             records[i].join(this);
10428         }
10429         var index = this.data.length;
10430         this.data.addAll(records);
10431         this.fireEvent("add", this, records, index);
10432     },
10433
10434     /**
10435      * Remove a Record from the Store and fires the remove event.
10436      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10437      */
10438     remove : function(record){
10439         var index = this.data.indexOf(record);
10440         this.data.removeAt(index);
10441         if(this.pruneModifiedRecords){
10442             this.modified.remove(record);
10443         }
10444         this.fireEvent("remove", this, record, index);
10445     },
10446
10447     /**
10448      * Remove all Records from the Store and fires the clear event.
10449      */
10450     removeAll : function(){
10451         this.data.clear();
10452         if(this.pruneModifiedRecords){
10453             this.modified = [];
10454         }
10455         this.fireEvent("clear", this);
10456     },
10457
10458     /**
10459      * Inserts Records to the Store at the given index and fires the add event.
10460      * @param {Number} index The start index at which to insert the passed Records.
10461      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10462      */
10463     insert : function(index, records){
10464         records = [].concat(records);
10465         for(var i = 0, len = records.length; i < len; i++){
10466             this.data.insert(index, records[i]);
10467             records[i].join(this);
10468         }
10469         this.fireEvent("add", this, records, index);
10470     },
10471
10472     /**
10473      * Get the index within the cache of the passed Record.
10474      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10475      * @return {Number} The index of the passed Record. Returns -1 if not found.
10476      */
10477     indexOf : function(record){
10478         return this.data.indexOf(record);
10479     },
10480
10481     /**
10482      * Get the index within the cache of the Record with the passed id.
10483      * @param {String} id The id of the Record to find.
10484      * @return {Number} The index of the Record. Returns -1 if not found.
10485      */
10486     indexOfId : function(id){
10487         return this.data.indexOfKey(id);
10488     },
10489
10490     /**
10491      * Get the Record with the specified id.
10492      * @param {String} id The id of the Record to find.
10493      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10494      */
10495     getById : function(id){
10496         return this.data.key(id);
10497     },
10498
10499     /**
10500      * Get the Record at the specified index.
10501      * @param {Number} index The index of the Record to find.
10502      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10503      */
10504     getAt : function(index){
10505         return this.data.itemAt(index);
10506     },
10507
10508     /**
10509      * Returns a range of Records between specified indices.
10510      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10511      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10512      * @return {Roo.data.Record[]} An array of Records
10513      */
10514     getRange : function(start, end){
10515         return this.data.getRange(start, end);
10516     },
10517
10518     // private
10519     storeOptions : function(o){
10520         o = Roo.apply({}, o);
10521         delete o.callback;
10522         delete o.scope;
10523         this.lastOptions = o;
10524     },
10525
10526     /**
10527      * Loads the Record cache from the configured Proxy using the configured Reader.
10528      * <p>
10529      * If using remote paging, then the first load call must specify the <em>start</em>
10530      * and <em>limit</em> properties in the options.params property to establish the initial
10531      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10532      * <p>
10533      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10534      * and this call will return before the new data has been loaded. Perform any post-processing
10535      * in a callback function, or in a "load" event handler.</strong>
10536      * <p>
10537      * @param {Object} options An object containing properties which control loading options:<ul>
10538      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10539      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10540      * passed the following arguments:<ul>
10541      * <li>r : Roo.data.Record[]</li>
10542      * <li>options: Options object from the load call</li>
10543      * <li>success: Boolean success indicator</li></ul></li>
10544      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10545      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10546      * </ul>
10547      */
10548     load : function(options){
10549         options = options || {};
10550         if(this.fireEvent("beforeload", this, options) !== false){
10551             this.storeOptions(options);
10552             var p = Roo.apply(options.params || {}, this.baseParams);
10553             // if meta was not loaded from remote source.. try requesting it.
10554             if (!this.reader.metaFromRemote) {
10555                 p._requestMeta = 1;
10556             }
10557             if(this.sortInfo && this.remoteSort){
10558                 var pn = this.paramNames;
10559                 p[pn["sort"]] = this.sortInfo.field;
10560                 p[pn["dir"]] = this.sortInfo.direction;
10561             }
10562             if (this.multiSort) {
10563                 var pn = this.paramNames;
10564                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10565             }
10566             
10567             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10568         }
10569     },
10570
10571     /**
10572      * Reloads the Record cache from the configured Proxy using the configured Reader and
10573      * the options from the last load operation performed.
10574      * @param {Object} options (optional) An object containing properties which may override the options
10575      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10576      * the most recently used options are reused).
10577      */
10578     reload : function(options){
10579         this.load(Roo.applyIf(options||{}, this.lastOptions));
10580     },
10581
10582     // private
10583     // Called as a callback by the Reader during a load operation.
10584     loadRecords : function(o, options, success){
10585         if(!o || success === false){
10586             if(success !== false){
10587                 this.fireEvent("load", this, [], options, o);
10588             }
10589             if(options.callback){
10590                 options.callback.call(options.scope || this, [], options, false);
10591             }
10592             return;
10593         }
10594         // if data returned failure - throw an exception.
10595         if (o.success === false) {
10596             // show a message if no listener is registered.
10597             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10598                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10599             }
10600             // loadmask wil be hooked into this..
10601             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10602             return;
10603         }
10604         var r = o.records, t = o.totalRecords || r.length;
10605         
10606         this.fireEvent("beforeloadadd", this, r, options, o);
10607         
10608         if(!options || options.add !== true){
10609             if(this.pruneModifiedRecords){
10610                 this.modified = [];
10611             }
10612             for(var i = 0, len = r.length; i < len; i++){
10613                 r[i].join(this);
10614             }
10615             if(this.snapshot){
10616                 this.data = this.snapshot;
10617                 delete this.snapshot;
10618             }
10619             this.data.clear();
10620             this.data.addAll(r);
10621             this.totalLength = t;
10622             this.applySort();
10623             this.fireEvent("datachanged", this);
10624         }else{
10625             this.totalLength = Math.max(t, this.data.length+r.length);
10626             this.add(r);
10627         }
10628         this.fireEvent("load", this, r, options, o);
10629         if(options.callback){
10630             options.callback.call(options.scope || this, r, options, true);
10631         }
10632     },
10633
10634
10635     /**
10636      * Loads data from a passed data block. A Reader which understands the format of the data
10637      * must have been configured in the constructor.
10638      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10639      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10640      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10641      */
10642     loadData : function(o, append){
10643         var r = this.reader.readRecords(o);
10644         this.loadRecords(r, {add: append}, true);
10645     },
10646
10647     /**
10648      * Gets the number of cached records.
10649      * <p>
10650      * <em>If using paging, this may not be the total size of the dataset. If the data object
10651      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10652      * the data set size</em>
10653      */
10654     getCount : function(){
10655         return this.data.length || 0;
10656     },
10657
10658     /**
10659      * Gets the total number of records in the dataset as returned by the server.
10660      * <p>
10661      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10662      * the dataset size</em>
10663      */
10664     getTotalCount : function(){
10665         return this.totalLength || 0;
10666     },
10667
10668     /**
10669      * Returns the sort state of the Store as an object with two properties:
10670      * <pre><code>
10671  field {String} The name of the field by which the Records are sorted
10672  direction {String} The sort order, "ASC" or "DESC"
10673      * </code></pre>
10674      */
10675     getSortState : function(){
10676         return this.sortInfo;
10677     },
10678
10679     // private
10680     applySort : function(){
10681         if(this.sortInfo && !this.remoteSort){
10682             var s = this.sortInfo, f = s.field;
10683             var st = this.fields.get(f).sortType;
10684             var fn = function(r1, r2){
10685                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10686                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10687             };
10688             this.data.sort(s.direction, fn);
10689             if(this.snapshot && this.snapshot != this.data){
10690                 this.snapshot.sort(s.direction, fn);
10691             }
10692         }
10693     },
10694
10695     /**
10696      * Sets the default sort column and order to be used by the next load operation.
10697      * @param {String} fieldName The name of the field to sort by.
10698      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10699      */
10700     setDefaultSort : function(field, dir){
10701         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10702     },
10703
10704     /**
10705      * Sort the Records.
10706      * If remote sorting is used, the sort is performed on the server, and the cache is
10707      * reloaded. If local sorting is used, the cache is sorted internally.
10708      * @param {String} fieldName The name of the field to sort by.
10709      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10710      */
10711     sort : function(fieldName, dir){
10712         var f = this.fields.get(fieldName);
10713         if(!dir){
10714             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10715             
10716             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10717                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10718             }else{
10719                 dir = f.sortDir;
10720             }
10721         }
10722         this.sortToggle[f.name] = dir;
10723         this.sortInfo = {field: f.name, direction: dir};
10724         if(!this.remoteSort){
10725             this.applySort();
10726             this.fireEvent("datachanged", this);
10727         }else{
10728             this.load(this.lastOptions);
10729         }
10730     },
10731
10732     /**
10733      * Calls the specified function for each of the Records in the cache.
10734      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10735      * Returning <em>false</em> aborts and exits the iteration.
10736      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10737      */
10738     each : function(fn, scope){
10739         this.data.each(fn, scope);
10740     },
10741
10742     /**
10743      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10744      * (e.g., during paging).
10745      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10746      */
10747     getModifiedRecords : function(){
10748         return this.modified;
10749     },
10750
10751     // private
10752     createFilterFn : function(property, value, anyMatch){
10753         if(!value.exec){ // not a regex
10754             value = String(value);
10755             if(value.length == 0){
10756                 return false;
10757             }
10758             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10759         }
10760         return function(r){
10761             return value.test(r.data[property]);
10762         };
10763     },
10764
10765     /**
10766      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10767      * @param {String} property A field on your records
10768      * @param {Number} start The record index to start at (defaults to 0)
10769      * @param {Number} end The last record index to include (defaults to length - 1)
10770      * @return {Number} The sum
10771      */
10772     sum : function(property, start, end){
10773         var rs = this.data.items, v = 0;
10774         start = start || 0;
10775         end = (end || end === 0) ? end : rs.length-1;
10776
10777         for(var i = start; i <= end; i++){
10778             v += (rs[i].data[property] || 0);
10779         }
10780         return v;
10781     },
10782
10783     /**
10784      * Filter the records by a specified property.
10785      * @param {String} field A field on your records
10786      * @param {String/RegExp} value Either a string that the field
10787      * should start with or a RegExp to test against the field
10788      * @param {Boolean} anyMatch True to match any part not just the beginning
10789      */
10790     filter : function(property, value, anyMatch){
10791         var fn = this.createFilterFn(property, value, anyMatch);
10792         return fn ? this.filterBy(fn) : this.clearFilter();
10793     },
10794
10795     /**
10796      * Filter by a function. The specified function will be called with each
10797      * record in this data source. If the function returns true the record is included,
10798      * otherwise it is filtered.
10799      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10800      * @param {Object} scope (optional) The scope of the function (defaults to this)
10801      */
10802     filterBy : function(fn, scope){
10803         this.snapshot = this.snapshot || this.data;
10804         this.data = this.queryBy(fn, scope||this);
10805         this.fireEvent("datachanged", this);
10806     },
10807
10808     /**
10809      * Query the records by a specified property.
10810      * @param {String} field A field on your records
10811      * @param {String/RegExp} value Either a string that the field
10812      * should start with or a RegExp to test against the field
10813      * @param {Boolean} anyMatch True to match any part not just the beginning
10814      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10815      */
10816     query : function(property, value, anyMatch){
10817         var fn = this.createFilterFn(property, value, anyMatch);
10818         return fn ? this.queryBy(fn) : this.data.clone();
10819     },
10820
10821     /**
10822      * Query by a function. The specified function will be called with each
10823      * record in this data source. If the function returns true the record is included
10824      * in the results.
10825      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10826      * @param {Object} scope (optional) The scope of the function (defaults to this)
10827       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10828      **/
10829     queryBy : function(fn, scope){
10830         var data = this.snapshot || this.data;
10831         return data.filterBy(fn, scope||this);
10832     },
10833
10834     /**
10835      * Collects unique values for a particular dataIndex from this store.
10836      * @param {String} dataIndex The property to collect
10837      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10838      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10839      * @return {Array} An array of the unique values
10840      **/
10841     collect : function(dataIndex, allowNull, bypassFilter){
10842         var d = (bypassFilter === true && this.snapshot) ?
10843                 this.snapshot.items : this.data.items;
10844         var v, sv, r = [], l = {};
10845         for(var i = 0, len = d.length; i < len; i++){
10846             v = d[i].data[dataIndex];
10847             sv = String(v);
10848             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10849                 l[sv] = true;
10850                 r[r.length] = v;
10851             }
10852         }
10853         return r;
10854     },
10855
10856     /**
10857      * Revert to a view of the Record cache with no filtering applied.
10858      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10859      */
10860     clearFilter : function(suppressEvent){
10861         if(this.snapshot && this.snapshot != this.data){
10862             this.data = this.snapshot;
10863             delete this.snapshot;
10864             if(suppressEvent !== true){
10865                 this.fireEvent("datachanged", this);
10866             }
10867         }
10868     },
10869
10870     // private
10871     afterEdit : function(record){
10872         if(this.modified.indexOf(record) == -1){
10873             this.modified.push(record);
10874         }
10875         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10876     },
10877     
10878     // private
10879     afterReject : function(record){
10880         this.modified.remove(record);
10881         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10882     },
10883
10884     // private
10885     afterCommit : function(record){
10886         this.modified.remove(record);
10887         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10888     },
10889
10890     /**
10891      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10892      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10893      */
10894     commitChanges : function(){
10895         var m = this.modified.slice(0);
10896         this.modified = [];
10897         for(var i = 0, len = m.length; i < len; i++){
10898             m[i].commit();
10899         }
10900     },
10901
10902     /**
10903      * Cancel outstanding changes on all changed records.
10904      */
10905     rejectChanges : function(){
10906         var m = this.modified.slice(0);
10907         this.modified = [];
10908         for(var i = 0, len = m.length; i < len; i++){
10909             m[i].reject();
10910         }
10911     },
10912
10913     onMetaChange : function(meta, rtype, o){
10914         this.recordType = rtype;
10915         this.fields = rtype.prototype.fields;
10916         delete this.snapshot;
10917         this.sortInfo = meta.sortInfo || this.sortInfo;
10918         this.modified = [];
10919         this.fireEvent('metachange', this, this.reader.meta);
10920     },
10921     
10922     moveIndex : function(data, type)
10923     {
10924         var index = this.indexOf(data);
10925         
10926         var newIndex = index + type;
10927         
10928         this.remove(data);
10929         
10930         this.insert(newIndex, data);
10931         
10932     }
10933 });/*
10934  * Based on:
10935  * Ext JS Library 1.1.1
10936  * Copyright(c) 2006-2007, Ext JS, LLC.
10937  *
10938  * Originally Released Under LGPL - original licence link has changed is not relivant.
10939  *
10940  * Fork - LGPL
10941  * <script type="text/javascript">
10942  */
10943
10944 /**
10945  * @class Roo.data.SimpleStore
10946  * @extends Roo.data.Store
10947  * Small helper class to make creating Stores from Array data easier.
10948  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10949  * @cfg {Array} fields An array of field definition objects, or field name strings.
10950  * @cfg {Array} data The multi-dimensional array of data
10951  * @constructor
10952  * @param {Object} config
10953  */
10954 Roo.data.SimpleStore = function(config){
10955     Roo.data.SimpleStore.superclass.constructor.call(this, {
10956         isLocal : true,
10957         reader: new Roo.data.ArrayReader({
10958                 id: config.id
10959             },
10960             Roo.data.Record.create(config.fields)
10961         ),
10962         proxy : new Roo.data.MemoryProxy(config.data)
10963     });
10964     this.load();
10965 };
10966 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10967  * Based on:
10968  * Ext JS Library 1.1.1
10969  * Copyright(c) 2006-2007, Ext JS, LLC.
10970  *
10971  * Originally Released Under LGPL - original licence link has changed is not relivant.
10972  *
10973  * Fork - LGPL
10974  * <script type="text/javascript">
10975  */
10976
10977 /**
10978 /**
10979  * @extends Roo.data.Store
10980  * @class Roo.data.JsonStore
10981  * Small helper class to make creating Stores for JSON data easier. <br/>
10982 <pre><code>
10983 var store = new Roo.data.JsonStore({
10984     url: 'get-images.php',
10985     root: 'images',
10986     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10987 });
10988 </code></pre>
10989  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10990  * JsonReader and HttpProxy (unless inline data is provided).</b>
10991  * @cfg {Array} fields An array of field definition objects, or field name strings.
10992  * @constructor
10993  * @param {Object} config
10994  */
10995 Roo.data.JsonStore = function(c){
10996     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10997         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10998         reader: new Roo.data.JsonReader(c, c.fields)
10999     }));
11000 };
11001 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11002  * Based on:
11003  * Ext JS Library 1.1.1
11004  * Copyright(c) 2006-2007, Ext JS, LLC.
11005  *
11006  * Originally Released Under LGPL - original licence link has changed is not relivant.
11007  *
11008  * Fork - LGPL
11009  * <script type="text/javascript">
11010  */
11011
11012  
11013 Roo.data.Field = function(config){
11014     if(typeof config == "string"){
11015         config = {name: config};
11016     }
11017     Roo.apply(this, config);
11018     
11019     if(!this.type){
11020         this.type = "auto";
11021     }
11022     
11023     var st = Roo.data.SortTypes;
11024     // named sortTypes are supported, here we look them up
11025     if(typeof this.sortType == "string"){
11026         this.sortType = st[this.sortType];
11027     }
11028     
11029     // set default sortType for strings and dates
11030     if(!this.sortType){
11031         switch(this.type){
11032             case "string":
11033                 this.sortType = st.asUCString;
11034                 break;
11035             case "date":
11036                 this.sortType = st.asDate;
11037                 break;
11038             default:
11039                 this.sortType = st.none;
11040         }
11041     }
11042
11043     // define once
11044     var stripRe = /[\$,%]/g;
11045
11046     // prebuilt conversion function for this field, instead of
11047     // switching every time we're reading a value
11048     if(!this.convert){
11049         var cv, dateFormat = this.dateFormat;
11050         switch(this.type){
11051             case "":
11052             case "auto":
11053             case undefined:
11054                 cv = function(v){ return v; };
11055                 break;
11056             case "string":
11057                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11058                 break;
11059             case "int":
11060                 cv = function(v){
11061                     return v !== undefined && v !== null && v !== '' ?
11062                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11063                     };
11064                 break;
11065             case "float":
11066                 cv = function(v){
11067                     return v !== undefined && v !== null && v !== '' ?
11068                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11069                     };
11070                 break;
11071             case "bool":
11072             case "boolean":
11073                 cv = function(v){ return v === true || v === "true" || v == 1; };
11074                 break;
11075             case "date":
11076                 cv = function(v){
11077                     if(!v){
11078                         return '';
11079                     }
11080                     if(v instanceof Date){
11081                         return v;
11082                     }
11083                     if(dateFormat){
11084                         if(dateFormat == "timestamp"){
11085                             return new Date(v*1000);
11086                         }
11087                         return Date.parseDate(v, dateFormat);
11088                     }
11089                     var parsed = Date.parse(v);
11090                     return parsed ? new Date(parsed) : null;
11091                 };
11092              break;
11093             
11094         }
11095         this.convert = cv;
11096     }
11097 };
11098
11099 Roo.data.Field.prototype = {
11100     dateFormat: null,
11101     defaultValue: "",
11102     mapping: null,
11103     sortType : null,
11104     sortDir : "ASC"
11105 };/*
11106  * Based on:
11107  * Ext JS Library 1.1.1
11108  * Copyright(c) 2006-2007, Ext JS, LLC.
11109  *
11110  * Originally Released Under LGPL - original licence link has changed is not relivant.
11111  *
11112  * Fork - LGPL
11113  * <script type="text/javascript">
11114  */
11115  
11116 // Base class for reading structured data from a data source.  This class is intended to be
11117 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11118
11119 /**
11120  * @class Roo.data.DataReader
11121  * Base class for reading structured data from a data source.  This class is intended to be
11122  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11123  */
11124
11125 Roo.data.DataReader = function(meta, recordType){
11126     
11127     this.meta = meta;
11128     
11129     this.recordType = recordType instanceof Array ? 
11130         Roo.data.Record.create(recordType) : recordType;
11131 };
11132
11133 Roo.data.DataReader.prototype = {
11134      /**
11135      * Create an empty record
11136      * @param {Object} data (optional) - overlay some values
11137      * @return {Roo.data.Record} record created.
11138      */
11139     newRow :  function(d) {
11140         var da =  {};
11141         this.recordType.prototype.fields.each(function(c) {
11142             switch( c.type) {
11143                 case 'int' : da[c.name] = 0; break;
11144                 case 'date' : da[c.name] = new Date(); break;
11145                 case 'float' : da[c.name] = 0.0; break;
11146                 case 'boolean' : da[c.name] = false; break;
11147                 default : da[c.name] = ""; break;
11148             }
11149             
11150         });
11151         return new this.recordType(Roo.apply(da, d));
11152     }
11153     
11154 };/*
11155  * Based on:
11156  * Ext JS Library 1.1.1
11157  * Copyright(c) 2006-2007, Ext JS, LLC.
11158  *
11159  * Originally Released Under LGPL - original licence link has changed is not relivant.
11160  *
11161  * Fork - LGPL
11162  * <script type="text/javascript">
11163  */
11164
11165 /**
11166  * @class Roo.data.DataProxy
11167  * @extends Roo.data.Observable
11168  * This class is an abstract base class for implementations which provide retrieval of
11169  * unformatted data objects.<br>
11170  * <p>
11171  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11172  * (of the appropriate type which knows how to parse the data object) to provide a block of
11173  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11174  * <p>
11175  * Custom implementations must implement the load method as described in
11176  * {@link Roo.data.HttpProxy#load}.
11177  */
11178 Roo.data.DataProxy = function(){
11179     this.addEvents({
11180         /**
11181          * @event beforeload
11182          * Fires before a network request is made to retrieve a data object.
11183          * @param {Object} This DataProxy object.
11184          * @param {Object} params The params parameter to the load function.
11185          */
11186         beforeload : true,
11187         /**
11188          * @event load
11189          * Fires before the load method's callback is called.
11190          * @param {Object} This DataProxy object.
11191          * @param {Object} o The data object.
11192          * @param {Object} arg The callback argument object passed to the load function.
11193          */
11194         load : true,
11195         /**
11196          * @event loadexception
11197          * Fires if an Exception occurs during data retrieval.
11198          * @param {Object} This DataProxy object.
11199          * @param {Object} o The data object.
11200          * @param {Object} arg The callback argument object passed to the load function.
11201          * @param {Object} e The Exception.
11202          */
11203         loadexception : true
11204     });
11205     Roo.data.DataProxy.superclass.constructor.call(this);
11206 };
11207
11208 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11209
11210     /**
11211      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11212      */
11213 /*
11214  * Based on:
11215  * Ext JS Library 1.1.1
11216  * Copyright(c) 2006-2007, Ext JS, LLC.
11217  *
11218  * Originally Released Under LGPL - original licence link has changed is not relivant.
11219  *
11220  * Fork - LGPL
11221  * <script type="text/javascript">
11222  */
11223 /**
11224  * @class Roo.data.MemoryProxy
11225  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11226  * to the Reader when its load method is called.
11227  * @constructor
11228  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11229  */
11230 Roo.data.MemoryProxy = function(data){
11231     if (data.data) {
11232         data = data.data;
11233     }
11234     Roo.data.MemoryProxy.superclass.constructor.call(this);
11235     this.data = data;
11236 };
11237
11238 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11239     
11240     /**
11241      * Load data from the requested source (in this case an in-memory
11242      * data object passed to the constructor), read the data object into
11243      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11244      * process that block using the passed callback.
11245      * @param {Object} params This parameter is not used by the MemoryProxy class.
11246      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11247      * object into a block of Roo.data.Records.
11248      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11249      * The function must be passed <ul>
11250      * <li>The Record block object</li>
11251      * <li>The "arg" argument from the load function</li>
11252      * <li>A boolean success indicator</li>
11253      * </ul>
11254      * @param {Object} scope The scope in which to call the callback
11255      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11256      */
11257     load : function(params, reader, callback, scope, arg){
11258         params = params || {};
11259         var result;
11260         try {
11261             result = reader.readRecords(this.data);
11262         }catch(e){
11263             this.fireEvent("loadexception", this, arg, null, e);
11264             callback.call(scope, null, arg, false);
11265             return;
11266         }
11267         callback.call(scope, result, arg, true);
11268     },
11269     
11270     // private
11271     update : function(params, records){
11272         
11273     }
11274 });/*
11275  * Based on:
11276  * Ext JS Library 1.1.1
11277  * Copyright(c) 2006-2007, Ext JS, LLC.
11278  *
11279  * Originally Released Under LGPL - original licence link has changed is not relivant.
11280  *
11281  * Fork - LGPL
11282  * <script type="text/javascript">
11283  */
11284 /**
11285  * @class Roo.data.HttpProxy
11286  * @extends Roo.data.DataProxy
11287  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11288  * configured to reference a certain URL.<br><br>
11289  * <p>
11290  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11291  * from which the running page was served.<br><br>
11292  * <p>
11293  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11294  * <p>
11295  * Be aware that to enable the browser to parse an XML document, the server must set
11296  * the Content-Type header in the HTTP response to "text/xml".
11297  * @constructor
11298  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11299  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11300  * will be used to make the request.
11301  */
11302 Roo.data.HttpProxy = function(conn){
11303     Roo.data.HttpProxy.superclass.constructor.call(this);
11304     // is conn a conn config or a real conn?
11305     this.conn = conn;
11306     this.useAjax = !conn || !conn.events;
11307   
11308 };
11309
11310 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11311     // thse are take from connection...
11312     
11313     /**
11314      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11315      */
11316     /**
11317      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11318      * extra parameters to each request made by this object. (defaults to undefined)
11319      */
11320     /**
11321      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11322      *  to each request made by this object. (defaults to undefined)
11323      */
11324     /**
11325      * @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)
11326      */
11327     /**
11328      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11329      */
11330      /**
11331      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11332      * @type Boolean
11333      */
11334   
11335
11336     /**
11337      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11338      * @type Boolean
11339      */
11340     /**
11341      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11342      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11343      * a finer-grained basis than the DataProxy events.
11344      */
11345     getConnection : function(){
11346         return this.useAjax ? Roo.Ajax : this.conn;
11347     },
11348
11349     /**
11350      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11351      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11352      * process that block using the passed callback.
11353      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11354      * for the request to the remote server.
11355      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11356      * object into a block of Roo.data.Records.
11357      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11358      * The function must be passed <ul>
11359      * <li>The Record block object</li>
11360      * <li>The "arg" argument from the load function</li>
11361      * <li>A boolean success indicator</li>
11362      * </ul>
11363      * @param {Object} scope The scope in which to call the callback
11364      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11365      */
11366     load : function(params, reader, callback, scope, arg){
11367         if(this.fireEvent("beforeload", this, params) !== false){
11368             var  o = {
11369                 params : params || {},
11370                 request: {
11371                     callback : callback,
11372                     scope : scope,
11373                     arg : arg
11374                 },
11375                 reader: reader,
11376                 callback : this.loadResponse,
11377                 scope: this
11378             };
11379             if(this.useAjax){
11380                 Roo.applyIf(o, this.conn);
11381                 if(this.activeRequest){
11382                     Roo.Ajax.abort(this.activeRequest);
11383                 }
11384                 this.activeRequest = Roo.Ajax.request(o);
11385             }else{
11386                 this.conn.request(o);
11387             }
11388         }else{
11389             callback.call(scope||this, null, arg, false);
11390         }
11391     },
11392
11393     // private
11394     loadResponse : function(o, success, response){
11395         delete this.activeRequest;
11396         if(!success){
11397             this.fireEvent("loadexception", this, o, response);
11398             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11399             return;
11400         }
11401         var result;
11402         try {
11403             result = o.reader.read(response);
11404         }catch(e){
11405             this.fireEvent("loadexception", this, o, response, e);
11406             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11407             return;
11408         }
11409         
11410         this.fireEvent("load", this, o, o.request.arg);
11411         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11412     },
11413
11414     // private
11415     update : function(dataSet){
11416
11417     },
11418
11419     // private
11420     updateResponse : function(dataSet){
11421
11422     }
11423 });/*
11424  * Based on:
11425  * Ext JS Library 1.1.1
11426  * Copyright(c) 2006-2007, Ext JS, LLC.
11427  *
11428  * Originally Released Under LGPL - original licence link has changed is not relivant.
11429  *
11430  * Fork - LGPL
11431  * <script type="text/javascript">
11432  */
11433
11434 /**
11435  * @class Roo.data.ScriptTagProxy
11436  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11437  * other than the originating domain of the running page.<br><br>
11438  * <p>
11439  * <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
11440  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11441  * <p>
11442  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11443  * source code that is used as the source inside a &lt;script> tag.<br><br>
11444  * <p>
11445  * In order for the browser to process the returned data, the server must wrap the data object
11446  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11447  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11448  * depending on whether the callback name was passed:
11449  * <p>
11450  * <pre><code>
11451 boolean scriptTag = false;
11452 String cb = request.getParameter("callback");
11453 if (cb != null) {
11454     scriptTag = true;
11455     response.setContentType("text/javascript");
11456 } else {
11457     response.setContentType("application/x-json");
11458 }
11459 Writer out = response.getWriter();
11460 if (scriptTag) {
11461     out.write(cb + "(");
11462 }
11463 out.print(dataBlock.toJsonString());
11464 if (scriptTag) {
11465     out.write(");");
11466 }
11467 </pre></code>
11468  *
11469  * @constructor
11470  * @param {Object} config A configuration object.
11471  */
11472 Roo.data.ScriptTagProxy = function(config){
11473     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11474     Roo.apply(this, config);
11475     this.head = document.getElementsByTagName("head")[0];
11476 };
11477
11478 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11479
11480 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11481     /**
11482      * @cfg {String} url The URL from which to request the data object.
11483      */
11484     /**
11485      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11486      */
11487     timeout : 30000,
11488     /**
11489      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11490      * the server the name of the callback function set up by the load call to process the returned data object.
11491      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11492      * javascript output which calls this named function passing the data object as its only parameter.
11493      */
11494     callbackParam : "callback",
11495     /**
11496      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11497      * name to the request.
11498      */
11499     nocache : true,
11500
11501     /**
11502      * Load data from the configured URL, read the data object into
11503      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11504      * process that block using the passed callback.
11505      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11506      * for the request to the remote server.
11507      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11508      * object into a block of Roo.data.Records.
11509      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11510      * The function must be passed <ul>
11511      * <li>The Record block object</li>
11512      * <li>The "arg" argument from the load function</li>
11513      * <li>A boolean success indicator</li>
11514      * </ul>
11515      * @param {Object} scope The scope in which to call the callback
11516      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11517      */
11518     load : function(params, reader, callback, scope, arg){
11519         if(this.fireEvent("beforeload", this, params) !== false){
11520
11521             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11522
11523             var url = this.url;
11524             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11525             if(this.nocache){
11526                 url += "&_dc=" + (new Date().getTime());
11527             }
11528             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11529             var trans = {
11530                 id : transId,
11531                 cb : "stcCallback"+transId,
11532                 scriptId : "stcScript"+transId,
11533                 params : params,
11534                 arg : arg,
11535                 url : url,
11536                 callback : callback,
11537                 scope : scope,
11538                 reader : reader
11539             };
11540             var conn = this;
11541
11542             window[trans.cb] = function(o){
11543                 conn.handleResponse(o, trans);
11544             };
11545
11546             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11547
11548             if(this.autoAbort !== false){
11549                 this.abort();
11550             }
11551
11552             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11553
11554             var script = document.createElement("script");
11555             script.setAttribute("src", url);
11556             script.setAttribute("type", "text/javascript");
11557             script.setAttribute("id", trans.scriptId);
11558             this.head.appendChild(script);
11559
11560             this.trans = trans;
11561         }else{
11562             callback.call(scope||this, null, arg, false);
11563         }
11564     },
11565
11566     // private
11567     isLoading : function(){
11568         return this.trans ? true : false;
11569     },
11570
11571     /**
11572      * Abort the current server request.
11573      */
11574     abort : function(){
11575         if(this.isLoading()){
11576             this.destroyTrans(this.trans);
11577         }
11578     },
11579
11580     // private
11581     destroyTrans : function(trans, isLoaded){
11582         this.head.removeChild(document.getElementById(trans.scriptId));
11583         clearTimeout(trans.timeoutId);
11584         if(isLoaded){
11585             window[trans.cb] = undefined;
11586             try{
11587                 delete window[trans.cb];
11588             }catch(e){}
11589         }else{
11590             // if hasn't been loaded, wait for load to remove it to prevent script error
11591             window[trans.cb] = function(){
11592                 window[trans.cb] = undefined;
11593                 try{
11594                     delete window[trans.cb];
11595                 }catch(e){}
11596             };
11597         }
11598     },
11599
11600     // private
11601     handleResponse : function(o, trans){
11602         this.trans = false;
11603         this.destroyTrans(trans, true);
11604         var result;
11605         try {
11606             result = trans.reader.readRecords(o);
11607         }catch(e){
11608             this.fireEvent("loadexception", this, o, trans.arg, e);
11609             trans.callback.call(trans.scope||window, null, trans.arg, false);
11610             return;
11611         }
11612         this.fireEvent("load", this, o, trans.arg);
11613         trans.callback.call(trans.scope||window, result, trans.arg, true);
11614     },
11615
11616     // private
11617     handleFailure : function(trans){
11618         this.trans = false;
11619         this.destroyTrans(trans, false);
11620         this.fireEvent("loadexception", this, null, trans.arg);
11621         trans.callback.call(trans.scope||window, null, trans.arg, false);
11622     }
11623 });/*
11624  * Based on:
11625  * Ext JS Library 1.1.1
11626  * Copyright(c) 2006-2007, Ext JS, LLC.
11627  *
11628  * Originally Released Under LGPL - original licence link has changed is not relivant.
11629  *
11630  * Fork - LGPL
11631  * <script type="text/javascript">
11632  */
11633
11634 /**
11635  * @class Roo.data.JsonReader
11636  * @extends Roo.data.DataReader
11637  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11638  * based on mappings in a provided Roo.data.Record constructor.
11639  * 
11640  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11641  * in the reply previously. 
11642  * 
11643  * <p>
11644  * Example code:
11645  * <pre><code>
11646 var RecordDef = Roo.data.Record.create([
11647     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11648     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11649 ]);
11650 var myReader = new Roo.data.JsonReader({
11651     totalProperty: "results",    // The property which contains the total dataset size (optional)
11652     root: "rows",                // The property which contains an Array of row objects
11653     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11654 }, RecordDef);
11655 </code></pre>
11656  * <p>
11657  * This would consume a JSON file like this:
11658  * <pre><code>
11659 { 'results': 2, 'rows': [
11660     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11661     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11662 }
11663 </code></pre>
11664  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11665  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11666  * paged from the remote server.
11667  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11668  * @cfg {String} root name of the property which contains the Array of row objects.
11669  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11670  * @cfg {Array} fields Array of field definition objects
11671  * @constructor
11672  * Create a new JsonReader
11673  * @param {Object} meta Metadata configuration options
11674  * @param {Object} recordType Either an Array of field definition objects,
11675  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11676  */
11677 Roo.data.JsonReader = function(meta, recordType){
11678     
11679     meta = meta || {};
11680     // set some defaults:
11681     Roo.applyIf(meta, {
11682         totalProperty: 'total',
11683         successProperty : 'success',
11684         root : 'data',
11685         id : 'id'
11686     });
11687     
11688     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11689 };
11690 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11691     
11692     /**
11693      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11694      * Used by Store query builder to append _requestMeta to params.
11695      * 
11696      */
11697     metaFromRemote : false,
11698     /**
11699      * This method is only used by a DataProxy which has retrieved data from a remote server.
11700      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11701      * @return {Object} data A data block which is used by an Roo.data.Store object as
11702      * a cache of Roo.data.Records.
11703      */
11704     read : function(response){
11705         var json = response.responseText;
11706        
11707         var o = /* eval:var:o */ eval("("+json+")");
11708         if(!o) {
11709             throw {message: "JsonReader.read: Json object not found"};
11710         }
11711         
11712         if(o.metaData){
11713             
11714             delete this.ef;
11715             this.metaFromRemote = true;
11716             this.meta = o.metaData;
11717             this.recordType = Roo.data.Record.create(o.metaData.fields);
11718             this.onMetaChange(this.meta, this.recordType, o);
11719         }
11720         return this.readRecords(o);
11721     },
11722
11723     // private function a store will implement
11724     onMetaChange : function(meta, recordType, o){
11725
11726     },
11727
11728     /**
11729          * @ignore
11730          */
11731     simpleAccess: function(obj, subsc) {
11732         return obj[subsc];
11733     },
11734
11735         /**
11736          * @ignore
11737          */
11738     getJsonAccessor: function(){
11739         var re = /[\[\.]/;
11740         return function(expr) {
11741             try {
11742                 return(re.test(expr))
11743                     ? new Function("obj", "return obj." + expr)
11744                     : function(obj){
11745                         return obj[expr];
11746                     };
11747             } catch(e){}
11748             return Roo.emptyFn;
11749         };
11750     }(),
11751
11752     /**
11753      * Create a data block containing Roo.data.Records from an XML document.
11754      * @param {Object} o An object which contains an Array of row objects in the property specified
11755      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11756      * which contains the total size of the dataset.
11757      * @return {Object} data A data block which is used by an Roo.data.Store object as
11758      * a cache of Roo.data.Records.
11759      */
11760     readRecords : function(o){
11761         /**
11762          * After any data loads, the raw JSON data is available for further custom processing.
11763          * @type Object
11764          */
11765         this.o = o;
11766         var s = this.meta, Record = this.recordType,
11767             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11768
11769 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11770         if (!this.ef) {
11771             if(s.totalProperty) {
11772                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11773                 }
11774                 if(s.successProperty) {
11775                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11776                 }
11777                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11778                 if (s.id) {
11779                         var g = this.getJsonAccessor(s.id);
11780                         this.getId = function(rec) {
11781                                 var r = g(rec);  
11782                                 return (r === undefined || r === "") ? null : r;
11783                         };
11784                 } else {
11785                         this.getId = function(){return null;};
11786                 }
11787             this.ef = [];
11788             for(var jj = 0; jj < fl; jj++){
11789                 f = fi[jj];
11790                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11791                 this.ef[jj] = this.getJsonAccessor(map);
11792             }
11793         }
11794
11795         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11796         if(s.totalProperty){
11797             var vt = parseInt(this.getTotal(o), 10);
11798             if(!isNaN(vt)){
11799                 totalRecords = vt;
11800             }
11801         }
11802         if(s.successProperty){
11803             var vs = this.getSuccess(o);
11804             if(vs === false || vs === 'false'){
11805                 success = false;
11806             }
11807         }
11808         var records = [];
11809         for(var i = 0; i < c; i++){
11810                 var n = root[i];
11811             var values = {};
11812             var id = this.getId(n);
11813             for(var j = 0; j < fl; j++){
11814                 f = fi[j];
11815             var v = this.ef[j](n);
11816             if (!f.convert) {
11817                 Roo.log('missing convert for ' + f.name);
11818                 Roo.log(f);
11819                 continue;
11820             }
11821             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11822             }
11823             var record = new Record(values, id);
11824             record.json = n;
11825             records[i] = record;
11826         }
11827         return {
11828             raw : o,
11829             success : success,
11830             records : records,
11831             totalRecords : totalRecords
11832         };
11833     }
11834 });/*
11835  * Based on:
11836  * Ext JS Library 1.1.1
11837  * Copyright(c) 2006-2007, Ext JS, LLC.
11838  *
11839  * Originally Released Under LGPL - original licence link has changed is not relivant.
11840  *
11841  * Fork - LGPL
11842  * <script type="text/javascript">
11843  */
11844
11845 /**
11846  * @class Roo.data.ArrayReader
11847  * @extends Roo.data.DataReader
11848  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11849  * Each element of that Array represents a row of data fields. The
11850  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11851  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11852  * <p>
11853  * Example code:.
11854  * <pre><code>
11855 var RecordDef = Roo.data.Record.create([
11856     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11857     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11858 ]);
11859 var myReader = new Roo.data.ArrayReader({
11860     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11861 }, RecordDef);
11862 </code></pre>
11863  * <p>
11864  * This would consume an Array like this:
11865  * <pre><code>
11866 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11867   </code></pre>
11868  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11869  * @constructor
11870  * Create a new JsonReader
11871  * @param {Object} meta Metadata configuration options.
11872  * @param {Object} recordType Either an Array of field definition objects
11873  * as specified to {@link Roo.data.Record#create},
11874  * or an {@link Roo.data.Record} object
11875  * created using {@link Roo.data.Record#create}.
11876  */
11877 Roo.data.ArrayReader = function(meta, recordType){
11878     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11879 };
11880
11881 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11882     /**
11883      * Create a data block containing Roo.data.Records from an XML document.
11884      * @param {Object} o An Array of row objects which represents the dataset.
11885      * @return {Object} data A data block which is used by an Roo.data.Store object as
11886      * a cache of Roo.data.Records.
11887      */
11888     readRecords : function(o){
11889         var sid = this.meta ? this.meta.id : null;
11890         var recordType = this.recordType, fields = recordType.prototype.fields;
11891         var records = [];
11892         var root = o;
11893             for(var i = 0; i < root.length; i++){
11894                     var n = root[i];
11895                 var values = {};
11896                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11897                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11898                 var f = fields.items[j];
11899                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11900                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11901                 v = f.convert(v);
11902                 values[f.name] = v;
11903             }
11904                 var record = new recordType(values, id);
11905                 record.json = n;
11906                 records[records.length] = record;
11907             }
11908             return {
11909                 records : records,
11910                 totalRecords : records.length
11911             };
11912     }
11913 });/*
11914  * - LGPL
11915  * * 
11916  */
11917
11918 /**
11919  * @class Roo.bootstrap.ComboBox
11920  * @extends Roo.bootstrap.TriggerField
11921  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11922  * @cfg {Boolean} append (true|false) default false
11923  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11924  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11925  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11926  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11927  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11928  * @cfg {Boolean} animate default true
11929  * @cfg {Boolean} emptyResultText only for touch device
11930  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11931  * @constructor
11932  * Create a new ComboBox.
11933  * @param {Object} config Configuration options
11934  */
11935 Roo.bootstrap.ComboBox = function(config){
11936     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11937     this.addEvents({
11938         /**
11939          * @event expand
11940          * Fires when the dropdown list is expanded
11941              * @param {Roo.bootstrap.ComboBox} combo This combo box
11942              */
11943         'expand' : true,
11944         /**
11945          * @event collapse
11946          * Fires when the dropdown list is collapsed
11947              * @param {Roo.bootstrap.ComboBox} combo This combo box
11948              */
11949         'collapse' : true,
11950         /**
11951          * @event beforeselect
11952          * Fires before a list item is selected. Return false to cancel the selection.
11953              * @param {Roo.bootstrap.ComboBox} combo This combo box
11954              * @param {Roo.data.Record} record The data record returned from the underlying store
11955              * @param {Number} index The index of the selected item in the dropdown list
11956              */
11957         'beforeselect' : true,
11958         /**
11959          * @event select
11960          * Fires when a list item is selected
11961              * @param {Roo.bootstrap.ComboBox} combo This combo box
11962              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11963              * @param {Number} index The index of the selected item in the dropdown list
11964              */
11965         'select' : true,
11966         /**
11967          * @event beforequery
11968          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11969          * The event object passed has these properties:
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              * @param {String} query The query
11972              * @param {Boolean} forceAll true to force "all" query
11973              * @param {Boolean} cancel true to cancel the query
11974              * @param {Object} e The query event object
11975              */
11976         'beforequery': true,
11977          /**
11978          * @event add
11979          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11980              * @param {Roo.bootstrap.ComboBox} combo This combo box
11981              */
11982         'add' : true,
11983         /**
11984          * @event edit
11985          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11986              * @param {Roo.bootstrap.ComboBox} combo This combo box
11987              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11988              */
11989         'edit' : true,
11990         /**
11991          * @event remove
11992          * Fires when the remove value from the combobox array
11993              * @param {Roo.bootstrap.ComboBox} combo This combo box
11994              */
11995         'remove' : true,
11996         /**
11997          * @event afterremove
11998          * Fires when the remove value from the combobox array
11999              * @param {Roo.bootstrap.ComboBox} combo This combo box
12000              */
12001         'afterremove' : true,
12002         /**
12003          * @event specialfilter
12004          * Fires when specialfilter
12005             * @param {Roo.bootstrap.ComboBox} combo This combo box
12006             */
12007         'specialfilter' : true,
12008         /**
12009          * @event tick
12010          * Fires when tick the element
12011             * @param {Roo.bootstrap.ComboBox} combo This combo box
12012             */
12013         'tick' : true,
12014         /**
12015          * @event touchviewdisplay
12016          * Fires when touch view require special display (default is using displayField)
12017             * @param {Roo.bootstrap.ComboBox} combo This combo box
12018             * @param {Object} cfg set html .
12019             */
12020         'touchviewdisplay' : true
12021         
12022     });
12023     
12024     this.item = [];
12025     this.tickItems = [];
12026     
12027     this.selectedIndex = -1;
12028     if(this.mode == 'local'){
12029         if(config.queryDelay === undefined){
12030             this.queryDelay = 10;
12031         }
12032         if(config.minChars === undefined){
12033             this.minChars = 0;
12034         }
12035     }
12036 };
12037
12038 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12039      
12040     /**
12041      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12042      * rendering into an Roo.Editor, defaults to false)
12043      */
12044     /**
12045      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12046      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12047      */
12048     /**
12049      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12050      */
12051     /**
12052      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12053      * the dropdown list (defaults to undefined, with no header element)
12054      */
12055
12056      /**
12057      * @cfg {String/Roo.Template} tpl The template to use to render the output
12058      */
12059      
12060      /**
12061      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12062      */
12063     listWidth: undefined,
12064     /**
12065      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12066      * mode = 'remote' or 'text' if mode = 'local')
12067      */
12068     displayField: undefined,
12069     
12070     /**
12071      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12072      * mode = 'remote' or 'value' if mode = 'local'). 
12073      * Note: use of a valueField requires the user make a selection
12074      * in order for a value to be mapped.
12075      */
12076     valueField: undefined,
12077     /**
12078      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12079      */
12080     modalTitle : '',
12081     
12082     /**
12083      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12084      * field's data value (defaults to the underlying DOM element's name)
12085      */
12086     hiddenName: undefined,
12087     /**
12088      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12089      */
12090     listClass: '',
12091     /**
12092      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12093      */
12094     selectedClass: 'active',
12095     
12096     /**
12097      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12098      */
12099     shadow:'sides',
12100     /**
12101      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12102      * anchor positions (defaults to 'tl-bl')
12103      */
12104     listAlign: 'tl-bl?',
12105     /**
12106      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12107      */
12108     maxHeight: 300,
12109     /**
12110      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12111      * query specified by the allQuery config option (defaults to 'query')
12112      */
12113     triggerAction: 'query',
12114     /**
12115      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12116      * (defaults to 4, does not apply if editable = false)
12117      */
12118     minChars : 4,
12119     /**
12120      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12121      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12122      */
12123     typeAhead: false,
12124     /**
12125      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12126      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12127      */
12128     queryDelay: 500,
12129     /**
12130      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12131      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12132      */
12133     pageSize: 0,
12134     /**
12135      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12136      * when editable = true (defaults to false)
12137      */
12138     selectOnFocus:false,
12139     /**
12140      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12141      */
12142     queryParam: 'query',
12143     /**
12144      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12145      * when mode = 'remote' (defaults to 'Loading...')
12146      */
12147     loadingText: 'Loading...',
12148     /**
12149      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12150      */
12151     resizable: false,
12152     /**
12153      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12154      */
12155     handleHeight : 8,
12156     /**
12157      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12158      * traditional select (defaults to true)
12159      */
12160     editable: true,
12161     /**
12162      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12163      */
12164     allQuery: '',
12165     /**
12166      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12167      */
12168     mode: 'remote',
12169     /**
12170      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12171      * listWidth has a higher value)
12172      */
12173     minListWidth : 70,
12174     /**
12175      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12176      * allow the user to set arbitrary text into the field (defaults to false)
12177      */
12178     forceSelection:false,
12179     /**
12180      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12181      * if typeAhead = true (defaults to 250)
12182      */
12183     typeAheadDelay : 250,
12184     /**
12185      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12186      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12187      */
12188     valueNotFoundText : undefined,
12189     /**
12190      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12191      */
12192     blockFocus : false,
12193     
12194     /**
12195      * @cfg {Boolean} disableClear Disable showing of clear button.
12196      */
12197     disableClear : false,
12198     /**
12199      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12200      */
12201     alwaysQuery : false,
12202     
12203     /**
12204      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12205      */
12206     multiple : false,
12207     
12208     /**
12209      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12210      */
12211     invalidClass : "has-warning",
12212     
12213     /**
12214      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12215      */
12216     validClass : "has-success",
12217     
12218     /**
12219      * @cfg {Boolean} specialFilter (true|false) special filter default false
12220      */
12221     specialFilter : false,
12222     
12223     /**
12224      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12225      */
12226     mobileTouchView : true,
12227     
12228     /**
12229      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12230      */
12231     useNativeIOS : false,
12232     
12233     ios_options : false,
12234     
12235     //private
12236     addicon : false,
12237     editicon: false,
12238     
12239     page: 0,
12240     hasQuery: false,
12241     append: false,
12242     loadNext: false,
12243     autoFocus : true,
12244     tickable : false,
12245     btnPosition : 'right',
12246     triggerList : true,
12247     showToggleBtn : true,
12248     animate : true,
12249     emptyResultText: 'Empty',
12250     triggerText : 'Select',
12251     
12252     // element that contains real text value.. (when hidden is used..)
12253     
12254     getAutoCreate : function()
12255     {
12256         var cfg = false;
12257         
12258         /*
12259          * Render classic select for iso
12260          */
12261         
12262         if(Roo.isIOS && this.useNativeIOS){
12263             cfg = this.getAutoCreateNativeIOS();
12264             return cfg;
12265         }
12266         
12267         /*
12268          * Touch Devices
12269          */
12270         
12271         if(Roo.isTouch && this.mobileTouchView){
12272             cfg = this.getAutoCreateTouchView();
12273             return cfg;;
12274         }
12275         
12276         /*
12277          *  Normal ComboBox
12278          */
12279         if(!this.tickable){
12280             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12281             return cfg;
12282         }
12283         
12284         /*
12285          *  ComboBox with tickable selections
12286          */
12287              
12288         var align = this.labelAlign || this.parentLabelAlign();
12289         
12290         cfg = {
12291             cls : 'form-group roo-combobox-tickable' //input-group
12292         };
12293         
12294         var buttons = {
12295             tag : 'div',
12296             cls : 'tickable-buttons',
12297             cn : [
12298                 {
12299                     tag : 'button',
12300                     type : 'button',
12301                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12302                     html : this.triggerText
12303                 },
12304                 {
12305                     tag : 'button',
12306                     type : 'button',
12307                     name : 'ok',
12308                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12309                     html : 'Done'
12310                 },
12311                 {
12312                     tag : 'button',
12313                     type : 'button',
12314                     name : 'cancel',
12315                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12316                     html : 'Cancel'
12317                 }
12318             ]
12319         };
12320         
12321         if(this.editable){
12322             buttons.cn.unshift({
12323                 tag: 'input',
12324                 cls: 'roo-select2-search-field-input'
12325             });
12326         }
12327         
12328         var _this = this;
12329         
12330         Roo.each(buttons.cn, function(c){
12331             if (_this.size) {
12332                 c.cls += ' btn-' + _this.size;
12333             }
12334
12335             if (_this.disabled) {
12336                 c.disabled = true;
12337             }
12338         });
12339         
12340         var box = {
12341             tag: 'div',
12342             cn: [
12343                 {
12344                     tag: 'input',
12345                     type : 'hidden',
12346                     cls: 'form-hidden-field'
12347                 },
12348                 {
12349                     tag: 'ul',
12350                     cls: 'roo-select2-choices',
12351                     cn:[
12352                         {
12353                             tag: 'li',
12354                             cls: 'roo-select2-search-field',
12355                             cn: [
12356
12357                                 buttons
12358                             ]
12359                         }
12360                     ]
12361                 }
12362             ]
12363         };
12364         
12365         var combobox = {
12366             cls: 'roo-select2-container input-group roo-select2-container-multi',
12367             cn: [
12368                 box
12369 //                {
12370 //                    tag: 'ul',
12371 //                    cls: 'typeahead typeahead-long dropdown-menu',
12372 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12373 //                }
12374             ]
12375         };
12376         
12377         if(this.hasFeedback && !this.allowBlank){
12378             
12379             var feedback = {
12380                 tag: 'span',
12381                 cls: 'glyphicon form-control-feedback'
12382             };
12383
12384             combobox.cn.push(feedback);
12385         }
12386         
12387         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12388             
12389 //                Roo.log("left and has label");
12390             cfg.cn = [
12391                 {
12392                     tag : 'i',
12393                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12394                     tooltip : 'This field is required'
12395                 },
12396                 {
12397                     tag: 'label',
12398                     'for' :  id,
12399                     cls : 'control-label col-sm-' + this.labelWidth,
12400                     html : this.fieldLabel
12401
12402                 },
12403                 {
12404                     cls : "col-sm-" + (12 - this.labelWidth), 
12405                     cn: [
12406                         combobox
12407                     ]
12408                 }
12409
12410             ];
12411
12412             if(this.indicatorpos == 'right'){
12413                 
12414                 cfg.cn = [
12415                     {
12416                         tag: 'label',
12417                         'for' :  id,
12418                         cls : 'control-label col-sm-' + this.labelWidth,
12419                         html : this.fieldLabel
12420
12421                     },
12422                     {
12423                         tag : 'i',
12424                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12425                         tooltip : 'This field is required'
12426                     },
12427                     {
12428                         cls : "col-sm-" + (12 - this.labelWidth), 
12429                         cn: [
12430                             combobox
12431                         ]
12432                     }
12433
12434                 ];
12435             
12436             }
12437                 
12438                 
12439         } else if ( this.fieldLabel.length) {
12440 //                Roo.log(" label");
12441                  cfg.cn = [
12442                     {
12443                         tag : 'i',
12444                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12445                         tooltip : 'This field is required'
12446                     },
12447                     {
12448                         tag: 'label',
12449                         //cls : 'input-group-addon',
12450                         html : this.fieldLabel
12451                         
12452                     },
12453                     
12454                     combobox
12455                     
12456                 ];
12457                 
12458                 if(this.indicatorpos == 'right'){
12459                     
12460                     cfg.cn = [
12461                         {
12462                             tag: 'label',
12463                             //cls : 'input-group-addon',
12464                             html : this.fieldLabel
12465
12466                         },
12467                         
12468                         {
12469                             tag : 'i',
12470                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12471                             tooltip : 'This field is required'
12472                         },
12473                         
12474                         combobox
12475
12476                     ];
12477                 
12478                 }
12479
12480         } else {
12481             
12482 //                Roo.log(" no label && no align");
12483                 cfg = combobox
12484                      
12485                 
12486         }
12487          
12488         var settings=this;
12489         ['xs','sm','md','lg'].map(function(size){
12490             if (settings[size]) {
12491                 cfg.cls += ' col-' + size + '-' + settings[size];
12492             }
12493         });
12494         
12495         return cfg;
12496         
12497     },
12498     
12499     _initEventsCalled : false,
12500     
12501     // private
12502     initEvents: function()
12503     {   
12504         if (this._initEventsCalled) { // as we call render... prevent looping...
12505             return;
12506         }
12507         this._initEventsCalled = true;
12508         
12509         if (!this.store) {
12510             throw "can not find store for combo";
12511         }
12512         
12513         this.store = Roo.factory(this.store, Roo.data);
12514         
12515         // if we are building from html. then this element is so complex, that we can not really
12516         // use the rendered HTML.
12517         // so we have to trash and replace the previous code.
12518         if (Roo.XComponent.build_from_html) {
12519             
12520             // remove this element....
12521             var e = this.el.dom, k=0;
12522             while (e ) { e = e.previousSibling;  ++k;}
12523
12524             this.el.remove();
12525             
12526             this.el=false;
12527             this.rendered = false;
12528             
12529             this.render(this.parent().getChildContainer(true), k);
12530             
12531             
12532             
12533         }
12534         
12535         if(Roo.isIOS && this.useNativeIOS){
12536             this.initIOSView();
12537             return;
12538         }
12539         
12540         /*
12541          * Touch Devices
12542          */
12543         
12544         if(Roo.isTouch && this.mobileTouchView){
12545             this.initTouchView();
12546             return;
12547         }
12548         
12549         if(this.tickable){
12550             this.initTickableEvents();
12551             return;
12552         }
12553         
12554         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12555         
12556         if(this.hiddenName){
12557             
12558             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12559             
12560             this.hiddenField.dom.value =
12561                 this.hiddenValue !== undefined ? this.hiddenValue :
12562                 this.value !== undefined ? this.value : '';
12563
12564             // prevent input submission
12565             this.el.dom.removeAttribute('name');
12566             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12567              
12568              
12569         }
12570         //if(Roo.isGecko){
12571         //    this.el.dom.setAttribute('autocomplete', 'off');
12572         //}
12573         
12574         var cls = 'x-combo-list';
12575         
12576         //this.list = new Roo.Layer({
12577         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12578         //});
12579         
12580         var _this = this;
12581         
12582         (function(){
12583             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12584             _this.list.setWidth(lw);
12585         }).defer(100);
12586         
12587         this.list.on('mouseover', this.onViewOver, this);
12588         this.list.on('mousemove', this.onViewMove, this);
12589         
12590         this.list.on('scroll', this.onViewScroll, this);
12591         
12592         /*
12593         this.list.swallowEvent('mousewheel');
12594         this.assetHeight = 0;
12595
12596         if(this.title){
12597             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12598             this.assetHeight += this.header.getHeight();
12599         }
12600
12601         this.innerList = this.list.createChild({cls:cls+'-inner'});
12602         this.innerList.on('mouseover', this.onViewOver, this);
12603         this.innerList.on('mousemove', this.onViewMove, this);
12604         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12605         
12606         if(this.allowBlank && !this.pageSize && !this.disableClear){
12607             this.footer = this.list.createChild({cls:cls+'-ft'});
12608             this.pageTb = new Roo.Toolbar(this.footer);
12609            
12610         }
12611         if(this.pageSize){
12612             this.footer = this.list.createChild({cls:cls+'-ft'});
12613             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12614                     {pageSize: this.pageSize});
12615             
12616         }
12617         
12618         if (this.pageTb && this.allowBlank && !this.disableClear) {
12619             var _this = this;
12620             this.pageTb.add(new Roo.Toolbar.Fill(), {
12621                 cls: 'x-btn-icon x-btn-clear',
12622                 text: '&#160;',
12623                 handler: function()
12624                 {
12625                     _this.collapse();
12626                     _this.clearValue();
12627                     _this.onSelect(false, -1);
12628                 }
12629             });
12630         }
12631         if (this.footer) {
12632             this.assetHeight += this.footer.getHeight();
12633         }
12634         */
12635             
12636         if(!this.tpl){
12637             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12638         }
12639
12640         this.view = new Roo.View(this.list, this.tpl, {
12641             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12642         });
12643         //this.view.wrapEl.setDisplayed(false);
12644         this.view.on('click', this.onViewClick, this);
12645         
12646         
12647         
12648         this.store.on('beforeload', this.onBeforeLoad, this);
12649         this.store.on('load', this.onLoad, this);
12650         this.store.on('loadexception', this.onLoadException, this);
12651         /*
12652         if(this.resizable){
12653             this.resizer = new Roo.Resizable(this.list,  {
12654                pinned:true, handles:'se'
12655             });
12656             this.resizer.on('resize', function(r, w, h){
12657                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12658                 this.listWidth = w;
12659                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12660                 this.restrictHeight();
12661             }, this);
12662             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12663         }
12664         */
12665         if(!this.editable){
12666             this.editable = true;
12667             this.setEditable(false);
12668         }
12669         
12670         /*
12671         
12672         if (typeof(this.events.add.listeners) != 'undefined') {
12673             
12674             this.addicon = this.wrap.createChild(
12675                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12676        
12677             this.addicon.on('click', function(e) {
12678                 this.fireEvent('add', this);
12679             }, this);
12680         }
12681         if (typeof(this.events.edit.listeners) != 'undefined') {
12682             
12683             this.editicon = this.wrap.createChild(
12684                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12685             if (this.addicon) {
12686                 this.editicon.setStyle('margin-left', '40px');
12687             }
12688             this.editicon.on('click', function(e) {
12689                 
12690                 // we fire even  if inothing is selected..
12691                 this.fireEvent('edit', this, this.lastData );
12692                 
12693             }, this);
12694         }
12695         */
12696         
12697         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12698             "up" : function(e){
12699                 this.inKeyMode = true;
12700                 this.selectPrev();
12701             },
12702
12703             "down" : function(e){
12704                 if(!this.isExpanded()){
12705                     this.onTriggerClick();
12706                 }else{
12707                     this.inKeyMode = true;
12708                     this.selectNext();
12709                 }
12710             },
12711
12712             "enter" : function(e){
12713 //                this.onViewClick();
12714                 //return true;
12715                 this.collapse();
12716                 
12717                 if(this.fireEvent("specialkey", this, e)){
12718                     this.onViewClick(false);
12719                 }
12720                 
12721                 return true;
12722             },
12723
12724             "esc" : function(e){
12725                 this.collapse();
12726             },
12727
12728             "tab" : function(e){
12729                 this.collapse();
12730                 
12731                 if(this.fireEvent("specialkey", this, e)){
12732                     this.onViewClick(false);
12733                 }
12734                 
12735                 return true;
12736             },
12737
12738             scope : this,
12739
12740             doRelay : function(foo, bar, hname){
12741                 if(hname == 'down' || this.scope.isExpanded()){
12742                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12743                 }
12744                 return true;
12745             },
12746
12747             forceKeyDown: true
12748         });
12749         
12750         
12751         this.queryDelay = Math.max(this.queryDelay || 10,
12752                 this.mode == 'local' ? 10 : 250);
12753         
12754         
12755         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12756         
12757         if(this.typeAhead){
12758             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12759         }
12760         if(this.editable !== false){
12761             this.inputEl().on("keyup", this.onKeyUp, this);
12762         }
12763         if(this.forceSelection){
12764             this.inputEl().on('blur', this.doForce, this);
12765         }
12766         
12767         if(this.multiple){
12768             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12769             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12770         }
12771     },
12772     
12773     initTickableEvents: function()
12774     {   
12775         this.createList();
12776         
12777         if(this.hiddenName){
12778             
12779             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12780             
12781             this.hiddenField.dom.value =
12782                 this.hiddenValue !== undefined ? this.hiddenValue :
12783                 this.value !== undefined ? this.value : '';
12784
12785             // prevent input submission
12786             this.el.dom.removeAttribute('name');
12787             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12788              
12789              
12790         }
12791         
12792 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12793         
12794         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12795         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12796         if(this.triggerList){
12797             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12798         }
12799          
12800         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12801         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12802         
12803         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12804         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12805         
12806         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12807         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12808         
12809         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12810         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12811         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12812         
12813         this.okBtn.hide();
12814         this.cancelBtn.hide();
12815         
12816         var _this = this;
12817         
12818         (function(){
12819             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12820             _this.list.setWidth(lw);
12821         }).defer(100);
12822         
12823         this.list.on('mouseover', this.onViewOver, this);
12824         this.list.on('mousemove', this.onViewMove, this);
12825         
12826         this.list.on('scroll', this.onViewScroll, this);
12827         
12828         if(!this.tpl){
12829             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>';
12830         }
12831
12832         this.view = new Roo.View(this.list, this.tpl, {
12833             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12834         });
12835         
12836         //this.view.wrapEl.setDisplayed(false);
12837         this.view.on('click', this.onViewClick, this);
12838         
12839         
12840         
12841         this.store.on('beforeload', this.onBeforeLoad, this);
12842         this.store.on('load', this.onLoad, this);
12843         this.store.on('loadexception', this.onLoadException, this);
12844         
12845         if(this.editable){
12846             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12847                 "up" : function(e){
12848                     this.inKeyMode = true;
12849                     this.selectPrev();
12850                 },
12851
12852                 "down" : function(e){
12853                     this.inKeyMode = true;
12854                     this.selectNext();
12855                 },
12856
12857                 "enter" : function(e){
12858                     if(this.fireEvent("specialkey", this, e)){
12859                         this.onViewClick(false);
12860                     }
12861                     
12862                     return true;
12863                 },
12864
12865                 "esc" : function(e){
12866                     this.onTickableFooterButtonClick(e, false, false);
12867                 },
12868
12869                 "tab" : function(e){
12870                     this.fireEvent("specialkey", this, e);
12871                     
12872                     this.onTickableFooterButtonClick(e, false, false);
12873                     
12874                     return true;
12875                 },
12876
12877                 scope : this,
12878
12879                 doRelay : function(e, fn, key){
12880                     if(this.scope.isExpanded()){
12881                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12882                     }
12883                     return true;
12884                 },
12885
12886                 forceKeyDown: true
12887             });
12888         }
12889         
12890         this.queryDelay = Math.max(this.queryDelay || 10,
12891                 this.mode == 'local' ? 10 : 250);
12892         
12893         
12894         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12895         
12896         if(this.typeAhead){
12897             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12898         }
12899         
12900         if(this.editable !== false){
12901             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12902         }
12903         
12904     },
12905
12906     onDestroy : function(){
12907         if(this.view){
12908             this.view.setStore(null);
12909             this.view.el.removeAllListeners();
12910             this.view.el.remove();
12911             this.view.purgeListeners();
12912         }
12913         if(this.list){
12914             this.list.dom.innerHTML  = '';
12915         }
12916         
12917         if(this.store){
12918             this.store.un('beforeload', this.onBeforeLoad, this);
12919             this.store.un('load', this.onLoad, this);
12920             this.store.un('loadexception', this.onLoadException, this);
12921         }
12922         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12923     },
12924
12925     // private
12926     fireKey : function(e){
12927         if(e.isNavKeyPress() && !this.list.isVisible()){
12928             this.fireEvent("specialkey", this, e);
12929         }
12930     },
12931
12932     // private
12933     onResize: function(w, h){
12934 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12935 //        
12936 //        if(typeof w != 'number'){
12937 //            // we do not handle it!?!?
12938 //            return;
12939 //        }
12940 //        var tw = this.trigger.getWidth();
12941 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12942 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12943 //        var x = w - tw;
12944 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12945 //            
12946 //        //this.trigger.setStyle('left', x+'px');
12947 //        
12948 //        if(this.list && this.listWidth === undefined){
12949 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12950 //            this.list.setWidth(lw);
12951 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12952 //        }
12953         
12954     
12955         
12956     },
12957
12958     /**
12959      * Allow or prevent the user from directly editing the field text.  If false is passed,
12960      * the user will only be able to select from the items defined in the dropdown list.  This method
12961      * is the runtime equivalent of setting the 'editable' config option at config time.
12962      * @param {Boolean} value True to allow the user to directly edit the field text
12963      */
12964     setEditable : function(value){
12965         if(value == this.editable){
12966             return;
12967         }
12968         this.editable = value;
12969         if(!value){
12970             this.inputEl().dom.setAttribute('readOnly', true);
12971             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12972             this.inputEl().addClass('x-combo-noedit');
12973         }else{
12974             this.inputEl().dom.setAttribute('readOnly', false);
12975             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12976             this.inputEl().removeClass('x-combo-noedit');
12977         }
12978     },
12979
12980     // private
12981     
12982     onBeforeLoad : function(combo,opts){
12983         if(!this.hasFocus){
12984             return;
12985         }
12986          if (!opts.add) {
12987             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12988          }
12989         this.restrictHeight();
12990         this.selectedIndex = -1;
12991     },
12992
12993     // private
12994     onLoad : function(){
12995         
12996         this.hasQuery = false;
12997         
12998         if(!this.hasFocus){
12999             return;
13000         }
13001         
13002         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13003             this.loading.hide();
13004         }
13005              
13006         if(this.store.getCount() > 0){
13007             this.expand();
13008             this.restrictHeight();
13009             if(this.lastQuery == this.allQuery){
13010                 if(this.editable && !this.tickable){
13011                     this.inputEl().dom.select();
13012                 }
13013                 
13014                 if(
13015                     !this.selectByValue(this.value, true) &&
13016                     this.autoFocus && 
13017                     (
13018                         !this.store.lastOptions ||
13019                         typeof(this.store.lastOptions.add) == 'undefined' || 
13020                         this.store.lastOptions.add != true
13021                     )
13022                 ){
13023                     this.select(0, true);
13024                 }
13025             }else{
13026                 if(this.autoFocus){
13027                     this.selectNext();
13028                 }
13029                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13030                     this.taTask.delay(this.typeAheadDelay);
13031                 }
13032             }
13033         }else{
13034             this.onEmptyResults();
13035         }
13036         
13037         //this.el.focus();
13038     },
13039     // private
13040     onLoadException : function()
13041     {
13042         this.hasQuery = false;
13043         
13044         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13045             this.loading.hide();
13046         }
13047         
13048         if(this.tickable && this.editable){
13049             return;
13050         }
13051         
13052         this.collapse();
13053         // only causes errors at present
13054         //Roo.log(this.store.reader.jsonData);
13055         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13056             // fixme
13057             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13058         //}
13059         
13060         
13061     },
13062     // private
13063     onTypeAhead : function(){
13064         if(this.store.getCount() > 0){
13065             var r = this.store.getAt(0);
13066             var newValue = r.data[this.displayField];
13067             var len = newValue.length;
13068             var selStart = this.getRawValue().length;
13069             
13070             if(selStart != len){
13071                 this.setRawValue(newValue);
13072                 this.selectText(selStart, newValue.length);
13073             }
13074         }
13075     },
13076
13077     // private
13078     onSelect : function(record, index){
13079         
13080         if(this.fireEvent('beforeselect', this, record, index) !== false){
13081         
13082             this.setFromData(index > -1 ? record.data : false);
13083             
13084             this.collapse();
13085             this.fireEvent('select', this, record, index);
13086         }
13087     },
13088
13089     /**
13090      * Returns the currently selected field value or empty string if no value is set.
13091      * @return {String} value The selected value
13092      */
13093     getValue : function()
13094     {
13095         if(Roo.isIOS && this.useNativeIOS){
13096             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13097         }
13098         
13099         if(this.multiple){
13100             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13101         }
13102         
13103         if(this.valueField){
13104             return typeof this.value != 'undefined' ? this.value : '';
13105         }else{
13106             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13107         }
13108     },
13109     
13110     getRawValue : function()
13111     {
13112         if(Roo.isIOS && this.useNativeIOS){
13113             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13114         }
13115         
13116         var v = this.inputEl().getValue();
13117         
13118         return v;
13119     },
13120
13121     /**
13122      * Clears any text/value currently set in the field
13123      */
13124     clearValue : function(){
13125         
13126         if(this.hiddenField){
13127             this.hiddenField.dom.value = '';
13128         }
13129         this.value = '';
13130         this.setRawValue('');
13131         this.lastSelectionText = '';
13132         this.lastData = false;
13133         
13134         var close = this.closeTriggerEl();
13135         
13136         if(close){
13137             close.hide();
13138         }
13139         
13140         this.validate();
13141         
13142     },
13143
13144     /**
13145      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13146      * will be displayed in the field.  If the value does not match the data value of an existing item,
13147      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13148      * Otherwise the field will be blank (although the value will still be set).
13149      * @param {String} value The value to match
13150      */
13151     setValue : function(v)
13152     {
13153         if(Roo.isIOS && this.useNativeIOS){
13154             this.setIOSValue(v);
13155             return;
13156         }
13157         
13158         if(this.multiple){
13159             this.syncValue();
13160             return;
13161         }
13162         
13163         var text = v;
13164         if(this.valueField){
13165             var r = this.findRecord(this.valueField, v);
13166             if(r){
13167                 text = r.data[this.displayField];
13168             }else if(this.valueNotFoundText !== undefined){
13169                 text = this.valueNotFoundText;
13170             }
13171         }
13172         this.lastSelectionText = text;
13173         if(this.hiddenField){
13174             this.hiddenField.dom.value = v;
13175         }
13176         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13177         this.value = v;
13178         
13179         var close = this.closeTriggerEl();
13180         
13181         if(close){
13182             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13183         }
13184         
13185         this.validate();
13186     },
13187     /**
13188      * @property {Object} the last set data for the element
13189      */
13190     
13191     lastData : false,
13192     /**
13193      * Sets the value of the field based on a object which is related to the record format for the store.
13194      * @param {Object} value the value to set as. or false on reset?
13195      */
13196     setFromData : function(o){
13197         
13198         if(this.multiple){
13199             this.addItem(o);
13200             return;
13201         }
13202             
13203         var dv = ''; // display value
13204         var vv = ''; // value value..
13205         this.lastData = o;
13206         if (this.displayField) {
13207             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13208         } else {
13209             // this is an error condition!!!
13210             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13211         }
13212         
13213         if(this.valueField){
13214             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13215         }
13216         
13217         var close = this.closeTriggerEl();
13218         
13219         if(close){
13220             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13221         }
13222         
13223         if(this.hiddenField){
13224             this.hiddenField.dom.value = vv;
13225             
13226             this.lastSelectionText = dv;
13227             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13228             this.value = vv;
13229             return;
13230         }
13231         // no hidden field.. - we store the value in 'value', but still display
13232         // display field!!!!
13233         this.lastSelectionText = dv;
13234         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13235         this.value = vv;
13236         
13237         
13238         
13239     },
13240     // private
13241     reset : function(){
13242         // overridden so that last data is reset..
13243         
13244         if(this.multiple){
13245             this.clearItem();
13246             return;
13247         }
13248         
13249         this.setValue(this.originalValue);
13250         //this.clearInvalid();
13251         this.lastData = false;
13252         if (this.view) {
13253             this.view.clearSelections();
13254         }
13255         
13256         this.validate();
13257     },
13258     // private
13259     findRecord : function(prop, value){
13260         var record;
13261         if(this.store.getCount() > 0){
13262             this.store.each(function(r){
13263                 if(r.data[prop] == value){
13264                     record = r;
13265                     return false;
13266                 }
13267                 return true;
13268             });
13269         }
13270         return record;
13271     },
13272     
13273     getName: function()
13274     {
13275         // returns hidden if it's set..
13276         if (!this.rendered) {return ''};
13277         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13278         
13279     },
13280     // private
13281     onViewMove : function(e, t){
13282         this.inKeyMode = false;
13283     },
13284
13285     // private
13286     onViewOver : function(e, t){
13287         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13288             return;
13289         }
13290         var item = this.view.findItemFromChild(t);
13291         
13292         if(item){
13293             var index = this.view.indexOf(item);
13294             this.select(index, false);
13295         }
13296     },
13297
13298     // private
13299     onViewClick : function(view, doFocus, el, e)
13300     {
13301         var index = this.view.getSelectedIndexes()[0];
13302         
13303         var r = this.store.getAt(index);
13304         
13305         if(this.tickable){
13306             
13307             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13308                 return;
13309             }
13310             
13311             var rm = false;
13312             var _this = this;
13313             
13314             Roo.each(this.tickItems, function(v,k){
13315                 
13316                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13317                     Roo.log(v);
13318                     _this.tickItems.splice(k, 1);
13319                     
13320                     if(typeof(e) == 'undefined' && view == false){
13321                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13322                     }
13323                     
13324                     rm = true;
13325                     return;
13326                 }
13327             });
13328             
13329             if(rm){
13330                 return;
13331             }
13332             
13333             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13334                 this.tickItems.push(r.data);
13335             }
13336             
13337             if(typeof(e) == 'undefined' && view == false){
13338                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13339             }
13340                     
13341             return;
13342         }
13343         
13344         if(r){
13345             this.onSelect(r, index);
13346         }
13347         if(doFocus !== false && !this.blockFocus){
13348             this.inputEl().focus();
13349         }
13350     },
13351
13352     // private
13353     restrictHeight : function(){
13354         //this.innerList.dom.style.height = '';
13355         //var inner = this.innerList.dom;
13356         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13357         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13358         //this.list.beginUpdate();
13359         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13360         this.list.alignTo(this.inputEl(), this.listAlign);
13361         this.list.alignTo(this.inputEl(), this.listAlign);
13362         //this.list.endUpdate();
13363     },
13364
13365     // private
13366     onEmptyResults : function(){
13367         
13368         if(this.tickable && this.editable){
13369             this.restrictHeight();
13370             return;
13371         }
13372         
13373         this.collapse();
13374     },
13375
13376     /**
13377      * Returns true if the dropdown list is expanded, else false.
13378      */
13379     isExpanded : function(){
13380         return this.list.isVisible();
13381     },
13382
13383     /**
13384      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13385      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13386      * @param {String} value The data value of the item to select
13387      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13388      * selected item if it is not currently in view (defaults to true)
13389      * @return {Boolean} True if the value matched an item in the list, else false
13390      */
13391     selectByValue : function(v, scrollIntoView){
13392         if(v !== undefined && v !== null){
13393             var r = this.findRecord(this.valueField || this.displayField, v);
13394             if(r){
13395                 this.select(this.store.indexOf(r), scrollIntoView);
13396                 return true;
13397             }
13398         }
13399         return false;
13400     },
13401
13402     /**
13403      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13404      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13405      * @param {Number} index The zero-based index of the list item to select
13406      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13407      * selected item if it is not currently in view (defaults to true)
13408      */
13409     select : function(index, scrollIntoView){
13410         this.selectedIndex = index;
13411         this.view.select(index);
13412         if(scrollIntoView !== false){
13413             var el = this.view.getNode(index);
13414             /*
13415              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13416              */
13417             if(el){
13418                 this.list.scrollChildIntoView(el, false);
13419             }
13420         }
13421     },
13422
13423     // private
13424     selectNext : function(){
13425         var ct = this.store.getCount();
13426         if(ct > 0){
13427             if(this.selectedIndex == -1){
13428                 this.select(0);
13429             }else if(this.selectedIndex < ct-1){
13430                 this.select(this.selectedIndex+1);
13431             }
13432         }
13433     },
13434
13435     // private
13436     selectPrev : function(){
13437         var ct = this.store.getCount();
13438         if(ct > 0){
13439             if(this.selectedIndex == -1){
13440                 this.select(0);
13441             }else if(this.selectedIndex != 0){
13442                 this.select(this.selectedIndex-1);
13443             }
13444         }
13445     },
13446
13447     // private
13448     onKeyUp : function(e){
13449         if(this.editable !== false && !e.isSpecialKey()){
13450             this.lastKey = e.getKey();
13451             this.dqTask.delay(this.queryDelay);
13452         }
13453     },
13454
13455     // private
13456     validateBlur : function(){
13457         return !this.list || !this.list.isVisible();   
13458     },
13459
13460     // private
13461     initQuery : function(){
13462         
13463         var v = this.getRawValue();
13464         
13465         if(this.tickable && this.editable){
13466             v = this.tickableInputEl().getValue();
13467         }
13468         
13469         this.doQuery(v);
13470     },
13471
13472     // private
13473     doForce : function(){
13474         if(this.inputEl().dom.value.length > 0){
13475             this.inputEl().dom.value =
13476                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13477              
13478         }
13479     },
13480
13481     /**
13482      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13483      * query allowing the query action to be canceled if needed.
13484      * @param {String} query The SQL query to execute
13485      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13486      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13487      * saved in the current store (defaults to false)
13488      */
13489     doQuery : function(q, forceAll){
13490         
13491         if(q === undefined || q === null){
13492             q = '';
13493         }
13494         var qe = {
13495             query: q,
13496             forceAll: forceAll,
13497             combo: this,
13498             cancel:false
13499         };
13500         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13501             return false;
13502         }
13503         q = qe.query;
13504         
13505         forceAll = qe.forceAll;
13506         if(forceAll === true || (q.length >= this.minChars)){
13507             
13508             this.hasQuery = true;
13509             
13510             if(this.lastQuery != q || this.alwaysQuery){
13511                 this.lastQuery = q;
13512                 if(this.mode == 'local'){
13513                     this.selectedIndex = -1;
13514                     if(forceAll){
13515                         this.store.clearFilter();
13516                     }else{
13517                         
13518                         if(this.specialFilter){
13519                             this.fireEvent('specialfilter', this);
13520                             this.onLoad();
13521                             return;
13522                         }
13523                         
13524                         this.store.filter(this.displayField, q);
13525                     }
13526                     
13527                     this.store.fireEvent("datachanged", this.store);
13528                     
13529                     this.onLoad();
13530                     
13531                     
13532                 }else{
13533                     
13534                     this.store.baseParams[this.queryParam] = q;
13535                     
13536                     var options = {params : this.getParams(q)};
13537                     
13538                     if(this.loadNext){
13539                         options.add = true;
13540                         options.params.start = this.page * this.pageSize;
13541                     }
13542                     
13543                     this.store.load(options);
13544                     
13545                     /*
13546                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13547                      *  we should expand the list on onLoad
13548                      *  so command out it
13549                      */
13550 //                    this.expand();
13551                 }
13552             }else{
13553                 this.selectedIndex = -1;
13554                 this.onLoad();   
13555             }
13556         }
13557         
13558         this.loadNext = false;
13559     },
13560     
13561     // private
13562     getParams : function(q){
13563         var p = {};
13564         //p[this.queryParam] = q;
13565         
13566         if(this.pageSize){
13567             p.start = 0;
13568             p.limit = this.pageSize;
13569         }
13570         return p;
13571     },
13572
13573     /**
13574      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13575      */
13576     collapse : function(){
13577         if(!this.isExpanded()){
13578             return;
13579         }
13580         
13581         this.list.hide();
13582         
13583         if(this.tickable){
13584             this.hasFocus = false;
13585             this.okBtn.hide();
13586             this.cancelBtn.hide();
13587             this.trigger.show();
13588             
13589             if(this.editable){
13590                 this.tickableInputEl().dom.value = '';
13591                 this.tickableInputEl().blur();
13592             }
13593             
13594         }
13595         
13596         Roo.get(document).un('mousedown', this.collapseIf, this);
13597         Roo.get(document).un('mousewheel', this.collapseIf, this);
13598         if (!this.editable) {
13599             Roo.get(document).un('keydown', this.listKeyPress, this);
13600         }
13601         this.fireEvent('collapse', this);
13602         
13603         this.validate();
13604     },
13605
13606     // private
13607     collapseIf : function(e){
13608         var in_combo  = e.within(this.el);
13609         var in_list =  e.within(this.list);
13610         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13611         
13612         if (in_combo || in_list || is_list) {
13613             //e.stopPropagation();
13614             return;
13615         }
13616         
13617         if(this.tickable){
13618             this.onTickableFooterButtonClick(e, false, false);
13619         }
13620
13621         this.collapse();
13622         
13623     },
13624
13625     /**
13626      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13627      */
13628     expand : function(){
13629        
13630         if(this.isExpanded() || !this.hasFocus){
13631             return;
13632         }
13633         
13634         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13635         this.list.setWidth(lw);
13636         
13637         
13638          Roo.log('expand');
13639         
13640         this.list.show();
13641         
13642         this.restrictHeight();
13643         
13644         if(this.tickable){
13645             
13646             this.tickItems = Roo.apply([], this.item);
13647             
13648             this.okBtn.show();
13649             this.cancelBtn.show();
13650             this.trigger.hide();
13651             
13652             if(this.editable){
13653                 this.tickableInputEl().focus();
13654             }
13655             
13656         }
13657         
13658         Roo.get(document).on('mousedown', this.collapseIf, this);
13659         Roo.get(document).on('mousewheel', this.collapseIf, this);
13660         if (!this.editable) {
13661             Roo.get(document).on('keydown', this.listKeyPress, this);
13662         }
13663         
13664         this.fireEvent('expand', this);
13665     },
13666
13667     // private
13668     // Implements the default empty TriggerField.onTriggerClick function
13669     onTriggerClick : function(e)
13670     {
13671         Roo.log('trigger click');
13672         
13673         if(this.disabled || !this.triggerList){
13674             return;
13675         }
13676         
13677         this.page = 0;
13678         this.loadNext = false;
13679         
13680         if(this.isExpanded()){
13681             this.collapse();
13682             if (!this.blockFocus) {
13683                 this.inputEl().focus();
13684             }
13685             
13686         }else {
13687             this.hasFocus = true;
13688             if(this.triggerAction == 'all') {
13689                 this.doQuery(this.allQuery, true);
13690             } else {
13691                 this.doQuery(this.getRawValue());
13692             }
13693             if (!this.blockFocus) {
13694                 this.inputEl().focus();
13695             }
13696         }
13697     },
13698     
13699     onTickableTriggerClick : function(e)
13700     {
13701         if(this.disabled){
13702             return;
13703         }
13704         
13705         this.page = 0;
13706         this.loadNext = false;
13707         this.hasFocus = true;
13708         
13709         if(this.triggerAction == 'all') {
13710             this.doQuery(this.allQuery, true);
13711         } else {
13712             this.doQuery(this.getRawValue());
13713         }
13714     },
13715     
13716     onSearchFieldClick : function(e)
13717     {
13718         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13719             this.onTickableFooterButtonClick(e, false, false);
13720             return;
13721         }
13722         
13723         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13724             return;
13725         }
13726         
13727         this.page = 0;
13728         this.loadNext = false;
13729         this.hasFocus = true;
13730         
13731         if(this.triggerAction == 'all') {
13732             this.doQuery(this.allQuery, true);
13733         } else {
13734             this.doQuery(this.getRawValue());
13735         }
13736     },
13737     
13738     listKeyPress : function(e)
13739     {
13740         //Roo.log('listkeypress');
13741         // scroll to first matching element based on key pres..
13742         if (e.isSpecialKey()) {
13743             return false;
13744         }
13745         var k = String.fromCharCode(e.getKey()).toUpperCase();
13746         //Roo.log(k);
13747         var match  = false;
13748         var csel = this.view.getSelectedNodes();
13749         var cselitem = false;
13750         if (csel.length) {
13751             var ix = this.view.indexOf(csel[0]);
13752             cselitem  = this.store.getAt(ix);
13753             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13754                 cselitem = false;
13755             }
13756             
13757         }
13758         
13759         this.store.each(function(v) { 
13760             if (cselitem) {
13761                 // start at existing selection.
13762                 if (cselitem.id == v.id) {
13763                     cselitem = false;
13764                 }
13765                 return true;
13766             }
13767                 
13768             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13769                 match = this.store.indexOf(v);
13770                 return false;
13771             }
13772             return true;
13773         }, this);
13774         
13775         if (match === false) {
13776             return true; // no more action?
13777         }
13778         // scroll to?
13779         this.view.select(match);
13780         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13781         sn.scrollIntoView(sn.dom.parentNode, false);
13782     },
13783     
13784     onViewScroll : function(e, t){
13785         
13786         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){
13787             return;
13788         }
13789         
13790         this.hasQuery = true;
13791         
13792         this.loading = this.list.select('.loading', true).first();
13793         
13794         if(this.loading === null){
13795             this.list.createChild({
13796                 tag: 'div',
13797                 cls: 'loading roo-select2-more-results roo-select2-active',
13798                 html: 'Loading more results...'
13799             });
13800             
13801             this.loading = this.list.select('.loading', true).first();
13802             
13803             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13804             
13805             this.loading.hide();
13806         }
13807         
13808         this.loading.show();
13809         
13810         var _combo = this;
13811         
13812         this.page++;
13813         this.loadNext = true;
13814         
13815         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13816         
13817         return;
13818     },
13819     
13820     addItem : function(o)
13821     {   
13822         var dv = ''; // display value
13823         
13824         if (this.displayField) {
13825             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13826         } else {
13827             // this is an error condition!!!
13828             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13829         }
13830         
13831         if(!dv.length){
13832             return;
13833         }
13834         
13835         var choice = this.choices.createChild({
13836             tag: 'li',
13837             cls: 'roo-select2-search-choice',
13838             cn: [
13839                 {
13840                     tag: 'div',
13841                     html: dv
13842                 },
13843                 {
13844                     tag: 'a',
13845                     href: '#',
13846                     cls: 'roo-select2-search-choice-close',
13847                     tabindex: '-1'
13848                 }
13849             ]
13850             
13851         }, this.searchField);
13852         
13853         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13854         
13855         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13856         
13857         this.item.push(o);
13858         
13859         this.lastData = o;
13860         
13861         this.syncValue();
13862         
13863         this.inputEl().dom.value = '';
13864         
13865         this.validate();
13866     },
13867     
13868     onRemoveItem : function(e, _self, o)
13869     {
13870         e.preventDefault();
13871         
13872         this.lastItem = Roo.apply([], this.item);
13873         
13874         var index = this.item.indexOf(o.data) * 1;
13875         
13876         if( index < 0){
13877             Roo.log('not this item?!');
13878             return;
13879         }
13880         
13881         this.item.splice(index, 1);
13882         o.item.remove();
13883         
13884         this.syncValue();
13885         
13886         this.fireEvent('remove', this, e);
13887         
13888         this.validate();
13889         
13890     },
13891     
13892     syncValue : function()
13893     {
13894         if(!this.item.length){
13895             this.clearValue();
13896             return;
13897         }
13898             
13899         var value = [];
13900         var _this = this;
13901         Roo.each(this.item, function(i){
13902             if(_this.valueField){
13903                 value.push(i[_this.valueField]);
13904                 return;
13905             }
13906
13907             value.push(i);
13908         });
13909
13910         this.value = value.join(',');
13911
13912         if(this.hiddenField){
13913             this.hiddenField.dom.value = this.value;
13914         }
13915         
13916         this.store.fireEvent("datachanged", this.store);
13917         
13918         this.validate();
13919     },
13920     
13921     clearItem : function()
13922     {
13923         if(!this.multiple){
13924             return;
13925         }
13926         
13927         this.item = [];
13928         
13929         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13930            c.remove();
13931         });
13932         
13933         this.syncValue();
13934         
13935         this.validate();
13936         
13937         if(this.tickable && !Roo.isTouch){
13938             this.view.refresh();
13939         }
13940     },
13941     
13942     inputEl: function ()
13943     {
13944         if(Roo.isIOS && this.useNativeIOS){
13945             return this.el.select('select.roo-ios-select', true).first();
13946         }
13947         
13948         if(Roo.isTouch && this.mobileTouchView){
13949             return this.el.select('input.form-control',true).first();
13950         }
13951         
13952         if(this.tickable){
13953             return this.searchField;
13954         }
13955         
13956         return this.el.select('input.form-control',true).first();
13957     },
13958     
13959     onTickableFooterButtonClick : function(e, btn, el)
13960     {
13961         e.preventDefault();
13962         
13963         this.lastItem = Roo.apply([], this.item);
13964         
13965         if(btn && btn.name == 'cancel'){
13966             this.tickItems = Roo.apply([], this.item);
13967             this.collapse();
13968             return;
13969         }
13970         
13971         this.clearItem();
13972         
13973         var _this = this;
13974         
13975         Roo.each(this.tickItems, function(o){
13976             _this.addItem(o);
13977         });
13978         
13979         this.collapse();
13980         
13981     },
13982     
13983     validate : function()
13984     {
13985         var v = this.getRawValue();
13986         
13987         if(this.multiple){
13988             v = this.getValue();
13989         }
13990         
13991         if(this.disabled || this.allowBlank || v.length){
13992             this.markValid();
13993             return true;
13994         }
13995         
13996         this.markInvalid();
13997         return false;
13998     },
13999     
14000     tickableInputEl : function()
14001     {
14002         if(!this.tickable || !this.editable){
14003             return this.inputEl();
14004         }
14005         
14006         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14007     },
14008     
14009     
14010     getAutoCreateTouchView : function()
14011     {
14012         var id = Roo.id();
14013         
14014         var cfg = {
14015             cls: 'form-group' //input-group
14016         };
14017         
14018         var input =  {
14019             tag: 'input',
14020             id : id,
14021             type : this.inputType,
14022             cls : 'form-control x-combo-noedit',
14023             autocomplete: 'new-password',
14024             placeholder : this.placeholder || '',
14025             readonly : true
14026         };
14027         
14028         if (this.name) {
14029             input.name = this.name;
14030         }
14031         
14032         if (this.size) {
14033             input.cls += ' input-' + this.size;
14034         }
14035         
14036         if (this.disabled) {
14037             input.disabled = true;
14038         }
14039         
14040         var inputblock = {
14041             cls : '',
14042             cn : [
14043                 input
14044             ]
14045         };
14046         
14047         if(this.before){
14048             inputblock.cls += ' input-group';
14049             
14050             inputblock.cn.unshift({
14051                 tag :'span',
14052                 cls : 'input-group-addon',
14053                 html : this.before
14054             });
14055         }
14056         
14057         if(this.removable && !this.multiple){
14058             inputblock.cls += ' roo-removable';
14059             
14060             inputblock.cn.push({
14061                 tag: 'button',
14062                 html : 'x',
14063                 cls : 'roo-combo-removable-btn close'
14064             });
14065         }
14066
14067         if(this.hasFeedback && !this.allowBlank){
14068             
14069             inputblock.cls += ' has-feedback';
14070             
14071             inputblock.cn.push({
14072                 tag: 'span',
14073                 cls: 'glyphicon form-control-feedback'
14074             });
14075             
14076         }
14077         
14078         if (this.after) {
14079             
14080             inputblock.cls += (this.before) ? '' : ' input-group';
14081             
14082             inputblock.cn.push({
14083                 tag :'span',
14084                 cls : 'input-group-addon',
14085                 html : this.after
14086             });
14087         }
14088
14089         var box = {
14090             tag: 'div',
14091             cn: [
14092                 {
14093                     tag: 'input',
14094                     type : 'hidden',
14095                     cls: 'form-hidden-field'
14096                 },
14097                 inputblock
14098             ]
14099             
14100         };
14101         
14102         if(this.multiple){
14103             box = {
14104                 tag: 'div',
14105                 cn: [
14106                     {
14107                         tag: 'input',
14108                         type : 'hidden',
14109                         cls: 'form-hidden-field'
14110                     },
14111                     {
14112                         tag: 'ul',
14113                         cls: 'roo-select2-choices',
14114                         cn:[
14115                             {
14116                                 tag: 'li',
14117                                 cls: 'roo-select2-search-field',
14118                                 cn: [
14119
14120                                     inputblock
14121                                 ]
14122                             }
14123                         ]
14124                     }
14125                 ]
14126             }
14127         };
14128         
14129         var combobox = {
14130             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14131             cn: [
14132                 box
14133             ]
14134         };
14135         
14136         if(!this.multiple && this.showToggleBtn){
14137             
14138             var caret = {
14139                         tag: 'span',
14140                         cls: 'caret'
14141             };
14142             
14143             if (this.caret != false) {
14144                 caret = {
14145                      tag: 'i',
14146                      cls: 'fa fa-' + this.caret
14147                 };
14148                 
14149             }
14150             
14151             combobox.cn.push({
14152                 tag :'span',
14153                 cls : 'input-group-addon btn dropdown-toggle',
14154                 cn : [
14155                     caret,
14156                     {
14157                         tag: 'span',
14158                         cls: 'combobox-clear',
14159                         cn  : [
14160                             {
14161                                 tag : 'i',
14162                                 cls: 'icon-remove'
14163                             }
14164                         ]
14165                     }
14166                 ]
14167
14168             })
14169         }
14170         
14171         if(this.multiple){
14172             combobox.cls += ' roo-select2-container-multi';
14173         }
14174         
14175         var align = this.labelAlign || this.parentLabelAlign();
14176         
14177         cfg.cn = combobox;
14178         
14179         if(this.fieldLabel.length && this.labelWidth){
14180             
14181             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14182             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14183             
14184             cfg.cn = [
14185                 {
14186                    tag : 'i',
14187                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14188                    tooltip : 'This field is required'
14189                 },
14190                 {
14191                     tag: 'label',
14192                     cls : 'control-label ' + lw,
14193                     html : this.fieldLabel
14194
14195                 },
14196                 {
14197                     cls : cw, 
14198                     cn: [
14199                         combobox
14200                     ]
14201                 }
14202             ];
14203             
14204             if(this.indicatorpos == 'right'){
14205                 cfg.cn = [
14206                     {
14207                         tag: 'label',
14208                         cls : 'control-label ' + lw,
14209                         html : this.fieldLabel
14210
14211                     },
14212                     {
14213                        tag : 'i',
14214                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14215                        tooltip : 'This field is required'
14216                     },
14217                     {
14218                         cls : cw, 
14219                         cn: [
14220                             combobox
14221                         ]
14222                     }
14223                 ];
14224             }
14225         }
14226         
14227         var settings = this;
14228         
14229         ['xs','sm','md','lg'].map(function(size){
14230             if (settings[size]) {
14231                 cfg.cls += ' col-' + size + '-' + settings[size];
14232             }
14233         });
14234         
14235         return cfg;
14236     },
14237     
14238     initTouchView : function()
14239     {
14240         this.renderTouchView();
14241         
14242         this.touchViewEl.on('scroll', function(){
14243             this.el.dom.scrollTop = 0;
14244         }, this);
14245         
14246         this.originalValue = this.getValue();
14247         
14248         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14249         
14250         this.inputEl().on("click", this.showTouchView, this);
14251         this.triggerEl.on("click", this.showTouchView, this);
14252         
14253         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14254         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14255         
14256         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14257         
14258         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14259         this.store.on('load', this.onTouchViewLoad, this);
14260         this.store.on('loadexception', this.onTouchViewLoadException, this);
14261         
14262         if(this.hiddenName){
14263             
14264             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14265             
14266             this.hiddenField.dom.value =
14267                 this.hiddenValue !== undefined ? this.hiddenValue :
14268                 this.value !== undefined ? this.value : '';
14269         
14270             this.el.dom.removeAttribute('name');
14271             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14272         }
14273         
14274         if(this.multiple){
14275             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14276             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14277         }
14278         
14279         if(this.removable && !this.multiple){
14280             var close = this.closeTriggerEl();
14281             if(close){
14282                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14283                 close.on('click', this.removeBtnClick, this, close);
14284             }
14285         }
14286         /*
14287          * fix the bug in Safari iOS8
14288          */
14289         this.inputEl().on("focus", function(e){
14290             document.activeElement.blur();
14291         }, this);
14292         
14293         return;
14294         
14295         
14296     },
14297     
14298     renderTouchView : function()
14299     {
14300         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14301         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14302         
14303         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14304         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14305         
14306         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14307         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14308         this.touchViewBodyEl.setStyle('overflow', 'auto');
14309         
14310         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14311         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14312         
14313         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14314         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14315         
14316     },
14317     
14318     showTouchView : function()
14319     {
14320         if(this.disabled){
14321             return;
14322         }
14323         
14324         this.touchViewHeaderEl.hide();
14325
14326         if(this.modalTitle.length){
14327             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14328             this.touchViewHeaderEl.show();
14329         }
14330
14331         this.touchViewEl.show();
14332
14333         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14334         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14335                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14336
14337         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14338
14339         if(this.modalTitle.length){
14340             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14341         }
14342         
14343         this.touchViewBodyEl.setHeight(bodyHeight);
14344
14345         if(this.animate){
14346             var _this = this;
14347             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14348         }else{
14349             this.touchViewEl.addClass('in');
14350         }
14351
14352         this.doTouchViewQuery();
14353         
14354     },
14355     
14356     hideTouchView : function()
14357     {
14358         this.touchViewEl.removeClass('in');
14359
14360         if(this.animate){
14361             var _this = this;
14362             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14363         }else{
14364             this.touchViewEl.setStyle('display', 'none');
14365         }
14366         
14367     },
14368     
14369     setTouchViewValue : function()
14370     {
14371         if(this.multiple){
14372             this.clearItem();
14373         
14374             var _this = this;
14375
14376             Roo.each(this.tickItems, function(o){
14377                 this.addItem(o);
14378             }, this);
14379         }
14380         
14381         this.hideTouchView();
14382     },
14383     
14384     doTouchViewQuery : function()
14385     {
14386         var qe = {
14387             query: '',
14388             forceAll: true,
14389             combo: this,
14390             cancel:false
14391         };
14392         
14393         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14394             return false;
14395         }
14396         
14397         if(!this.alwaysQuery || this.mode == 'local'){
14398             this.onTouchViewLoad();
14399             return;
14400         }
14401         
14402         this.store.load();
14403     },
14404     
14405     onTouchViewBeforeLoad : function(combo,opts)
14406     {
14407         return;
14408     },
14409
14410     // private
14411     onTouchViewLoad : function()
14412     {
14413         if(this.store.getCount() < 1){
14414             this.onTouchViewEmptyResults();
14415             return;
14416         }
14417         
14418         this.clearTouchView();
14419         
14420         var rawValue = this.getRawValue();
14421         
14422         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14423         
14424         this.tickItems = [];
14425         
14426         this.store.data.each(function(d, rowIndex){
14427             var row = this.touchViewListGroup.createChild(template);
14428             
14429             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14430                 row.addClass(d.data.cls);
14431             }
14432             
14433             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14434                 var cfg = {
14435                     data : d.data,
14436                     html : d.data[this.displayField]
14437                 };
14438                 
14439                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14440                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14441                 }
14442             }
14443             row.removeClass('selected');
14444             if(!this.multiple && this.valueField &&
14445                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14446             {
14447                 // radio buttons..
14448                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14449                 row.addClass('selected');
14450             }
14451             
14452             if(this.multiple && this.valueField &&
14453                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14454             {
14455                 
14456                 // checkboxes...
14457                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14458                 this.tickItems.push(d.data);
14459             }
14460             
14461             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14462             
14463         }, this);
14464         
14465         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14466         
14467         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14468
14469         if(this.modalTitle.length){
14470             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14471         }
14472
14473         var listHeight = this.touchViewListGroup.getHeight();
14474         
14475         var _this = this;
14476         
14477         if(firstChecked && listHeight > bodyHeight){
14478             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14479         }
14480         
14481     },
14482     
14483     onTouchViewLoadException : function()
14484     {
14485         this.hideTouchView();
14486     },
14487     
14488     onTouchViewEmptyResults : function()
14489     {
14490         this.clearTouchView();
14491         
14492         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14493         
14494         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14495         
14496     },
14497     
14498     clearTouchView : function()
14499     {
14500         this.touchViewListGroup.dom.innerHTML = '';
14501     },
14502     
14503     onTouchViewClick : function(e, el, o)
14504     {
14505         e.preventDefault();
14506         
14507         var row = o.row;
14508         var rowIndex = o.rowIndex;
14509         
14510         var r = this.store.getAt(rowIndex);
14511         
14512         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14513             
14514             if(!this.multiple){
14515                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14516                     c.dom.removeAttribute('checked');
14517                 }, this);
14518
14519                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14520
14521                 this.setFromData(r.data);
14522
14523                 var close = this.closeTriggerEl();
14524
14525                 if(close){
14526                     close.show();
14527                 }
14528
14529                 this.hideTouchView();
14530
14531                 this.fireEvent('select', this, r, rowIndex);
14532
14533                 return;
14534             }
14535
14536             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14537                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14538                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14539                 return;
14540             }
14541
14542             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14543             this.addItem(r.data);
14544             this.tickItems.push(r.data);
14545         }
14546     },
14547     
14548     getAutoCreateNativeIOS : function()
14549     {
14550         var cfg = {
14551             cls: 'form-group' //input-group,
14552         };
14553         
14554         var combobox =  {
14555             tag: 'select',
14556             cls : 'roo-ios-select'
14557         };
14558         
14559         if (this.name) {
14560             combobox.name = this.name;
14561         }
14562         
14563         if (this.disabled) {
14564             combobox.disabled = true;
14565         }
14566         
14567         var settings = this;
14568         
14569         ['xs','sm','md','lg'].map(function(size){
14570             if (settings[size]) {
14571                 cfg.cls += ' col-' + size + '-' + settings[size];
14572             }
14573         });
14574         
14575         cfg.cn = combobox;
14576         
14577         return cfg;
14578         
14579     },
14580     
14581     initIOSView : function()
14582     {
14583         this.store.on('load', this.onIOSViewLoad, this);
14584         
14585         return;
14586     },
14587     
14588     onIOSViewLoad : function()
14589     {
14590         if(this.store.getCount() < 1){
14591             return;
14592         }
14593         
14594         this.clearIOSView();
14595         
14596         if(this.allowBlank) {
14597             
14598             var default_text = '-- SELECT --';
14599             
14600             var opt = this.inputEl().createChild({
14601                 tag: 'option',
14602                 value : 0,
14603                 html : default_text
14604             });
14605             
14606             var o = {};
14607             o[this.valueField] = 0;
14608             o[this.displayField] = default_text;
14609             
14610             this.ios_options.push({
14611                 data : o,
14612                 el : opt
14613             });
14614             
14615         }
14616         
14617         this.store.data.each(function(d, rowIndex){
14618             
14619             var html = '';
14620             
14621             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14622                 html = d.data[this.displayField];
14623             }
14624             
14625             var value = '';
14626             
14627             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14628                 value = d.data[this.valueField];
14629             }
14630             
14631             var option = {
14632                 tag: 'option',
14633                 value : value,
14634                 html : html
14635             };
14636             
14637             if(this.value == d.data[this.valueField]){
14638                 option['selected'] = true;
14639             }
14640             
14641             var opt = this.inputEl().createChild(option);
14642             
14643             this.ios_options.push({
14644                 data : d.data,
14645                 el : opt
14646             });
14647             
14648         }, this);
14649         
14650         this.inputEl().on('change', function(){
14651            this.fireEvent('select', this);
14652         }, this);
14653         
14654     },
14655     
14656     clearIOSView: function()
14657     {
14658         this.inputEl().dom.innerHTML = '';
14659         
14660         this.ios_options = [];
14661     },
14662     
14663     setIOSValue: function(v)
14664     {
14665         this.value = v;
14666         
14667         if(!this.ios_options){
14668             return;
14669         }
14670         
14671         Roo.each(this.ios_options, function(opts){
14672            
14673            opts.el.dom.removeAttribute('selected');
14674            
14675            if(opts.data[this.valueField] != v){
14676                return;
14677            }
14678            
14679            opts.el.dom.setAttribute('selected', true);
14680            
14681         }, this);
14682     }
14683
14684     /** 
14685     * @cfg {Boolean} grow 
14686     * @hide 
14687     */
14688     /** 
14689     * @cfg {Number} growMin 
14690     * @hide 
14691     */
14692     /** 
14693     * @cfg {Number} growMax 
14694     * @hide 
14695     */
14696     /**
14697      * @hide
14698      * @method autoSize
14699      */
14700 });
14701
14702 Roo.apply(Roo.bootstrap.ComboBox,  {
14703     
14704     header : {
14705         tag: 'div',
14706         cls: 'modal-header',
14707         cn: [
14708             {
14709                 tag: 'h4',
14710                 cls: 'modal-title'
14711             }
14712         ]
14713     },
14714     
14715     body : {
14716         tag: 'div',
14717         cls: 'modal-body',
14718         cn: [
14719             {
14720                 tag: 'ul',
14721                 cls: 'list-group'
14722             }
14723         ]
14724     },
14725     
14726     listItemRadio : {
14727         tag: 'li',
14728         cls: 'list-group-item',
14729         cn: [
14730             {
14731                 tag: 'span',
14732                 cls: 'roo-combobox-list-group-item-value'
14733             },
14734             {
14735                 tag: 'div',
14736                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14737                 cn: [
14738                     {
14739                         tag: 'input',
14740                         type: 'radio'
14741                     },
14742                     {
14743                         tag: 'label'
14744                     }
14745                 ]
14746             }
14747         ]
14748     },
14749     
14750     listItemCheckbox : {
14751         tag: 'li',
14752         cls: 'list-group-item',
14753         cn: [
14754             {
14755                 tag: 'span',
14756                 cls: 'roo-combobox-list-group-item-value'
14757             },
14758             {
14759                 tag: 'div',
14760                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14761                 cn: [
14762                     {
14763                         tag: 'input',
14764                         type: 'checkbox'
14765                     },
14766                     {
14767                         tag: 'label'
14768                     }
14769                 ]
14770             }
14771         ]
14772     },
14773     
14774     emptyResult : {
14775         tag: 'div',
14776         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14777     },
14778     
14779     footer : {
14780         tag: 'div',
14781         cls: 'modal-footer',
14782         cn: [
14783             {
14784                 tag: 'div',
14785                 cls: 'row',
14786                 cn: [
14787                     {
14788                         tag: 'div',
14789                         cls: 'col-xs-6 text-left',
14790                         cn: {
14791                             tag: 'button',
14792                             cls: 'btn btn-danger roo-touch-view-cancel',
14793                             html: 'Cancel'
14794                         }
14795                     },
14796                     {
14797                         tag: 'div',
14798                         cls: 'col-xs-6 text-right',
14799                         cn: {
14800                             tag: 'button',
14801                             cls: 'btn btn-success roo-touch-view-ok',
14802                             html: 'OK'
14803                         }
14804                     }
14805                 ]
14806             }
14807         ]
14808         
14809     }
14810 });
14811
14812 Roo.apply(Roo.bootstrap.ComboBox,  {
14813     
14814     touchViewTemplate : {
14815         tag: 'div',
14816         cls: 'modal fade roo-combobox-touch-view',
14817         cn: [
14818             {
14819                 tag: 'div',
14820                 cls: 'modal-dialog',
14821                 style : 'position:fixed', // we have to fix position....
14822                 cn: [
14823                     {
14824                         tag: 'div',
14825                         cls: 'modal-content',
14826                         cn: [
14827                             Roo.bootstrap.ComboBox.header,
14828                             Roo.bootstrap.ComboBox.body,
14829                             Roo.bootstrap.ComboBox.footer
14830                         ]
14831                     }
14832                 ]
14833             }
14834         ]
14835     }
14836 });/*
14837  * Based on:
14838  * Ext JS Library 1.1.1
14839  * Copyright(c) 2006-2007, Ext JS, LLC.
14840  *
14841  * Originally Released Under LGPL - original licence link has changed is not relivant.
14842  *
14843  * Fork - LGPL
14844  * <script type="text/javascript">
14845  */
14846
14847 /**
14848  * @class Roo.View
14849  * @extends Roo.util.Observable
14850  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14851  * This class also supports single and multi selection modes. <br>
14852  * Create a data model bound view:
14853  <pre><code>
14854  var store = new Roo.data.Store(...);
14855
14856  var view = new Roo.View({
14857     el : "my-element",
14858     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14859  
14860     singleSelect: true,
14861     selectedClass: "ydataview-selected",
14862     store: store
14863  });
14864
14865  // listen for node click?
14866  view.on("click", function(vw, index, node, e){
14867  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14868  });
14869
14870  // load XML data
14871  dataModel.load("foobar.xml");
14872  </code></pre>
14873  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14874  * <br><br>
14875  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14876  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14877  * 
14878  * Note: old style constructor is still suported (container, template, config)
14879  * 
14880  * @constructor
14881  * Create a new View
14882  * @param {Object} config The config object
14883  * 
14884  */
14885 Roo.View = function(config, depreciated_tpl, depreciated_config){
14886     
14887     this.parent = false;
14888     
14889     if (typeof(depreciated_tpl) == 'undefined') {
14890         // new way.. - universal constructor.
14891         Roo.apply(this, config);
14892         this.el  = Roo.get(this.el);
14893     } else {
14894         // old format..
14895         this.el  = Roo.get(config);
14896         this.tpl = depreciated_tpl;
14897         Roo.apply(this, depreciated_config);
14898     }
14899     this.wrapEl  = this.el.wrap().wrap();
14900     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14901     
14902     
14903     if(typeof(this.tpl) == "string"){
14904         this.tpl = new Roo.Template(this.tpl);
14905     } else {
14906         // support xtype ctors..
14907         this.tpl = new Roo.factory(this.tpl, Roo);
14908     }
14909     
14910     
14911     this.tpl.compile();
14912     
14913     /** @private */
14914     this.addEvents({
14915         /**
14916          * @event beforeclick
14917          * Fires before a click is processed. Returns false to cancel the default action.
14918          * @param {Roo.View} this
14919          * @param {Number} index The index of the target node
14920          * @param {HTMLElement} node The target node
14921          * @param {Roo.EventObject} e The raw event object
14922          */
14923             "beforeclick" : true,
14924         /**
14925          * @event click
14926          * Fires when a template node is clicked.
14927          * @param {Roo.View} this
14928          * @param {Number} index The index of the target node
14929          * @param {HTMLElement} node The target node
14930          * @param {Roo.EventObject} e The raw event object
14931          */
14932             "click" : true,
14933         /**
14934          * @event dblclick
14935          * Fires when a template node is double clicked.
14936          * @param {Roo.View} this
14937          * @param {Number} index The index of the target node
14938          * @param {HTMLElement} node The target node
14939          * @param {Roo.EventObject} e The raw event object
14940          */
14941             "dblclick" : true,
14942         /**
14943          * @event contextmenu
14944          * Fires when a template node is right clicked.
14945          * @param {Roo.View} this
14946          * @param {Number} index The index of the target node
14947          * @param {HTMLElement} node The target node
14948          * @param {Roo.EventObject} e The raw event object
14949          */
14950             "contextmenu" : true,
14951         /**
14952          * @event selectionchange
14953          * Fires when the selected nodes change.
14954          * @param {Roo.View} this
14955          * @param {Array} selections Array of the selected nodes
14956          */
14957             "selectionchange" : true,
14958     
14959         /**
14960          * @event beforeselect
14961          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14962          * @param {Roo.View} this
14963          * @param {HTMLElement} node The node to be selected
14964          * @param {Array} selections Array of currently selected nodes
14965          */
14966             "beforeselect" : true,
14967         /**
14968          * @event preparedata
14969          * Fires on every row to render, to allow you to change the data.
14970          * @param {Roo.View} this
14971          * @param {Object} data to be rendered (change this)
14972          */
14973           "preparedata" : true
14974           
14975           
14976         });
14977
14978
14979
14980     this.el.on({
14981         "click": this.onClick,
14982         "dblclick": this.onDblClick,
14983         "contextmenu": this.onContextMenu,
14984         scope:this
14985     });
14986
14987     this.selections = [];
14988     this.nodes = [];
14989     this.cmp = new Roo.CompositeElementLite([]);
14990     if(this.store){
14991         this.store = Roo.factory(this.store, Roo.data);
14992         this.setStore(this.store, true);
14993     }
14994     
14995     if ( this.footer && this.footer.xtype) {
14996            
14997          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14998         
14999         this.footer.dataSource = this.store;
15000         this.footer.container = fctr;
15001         this.footer = Roo.factory(this.footer, Roo);
15002         fctr.insertFirst(this.el);
15003         
15004         // this is a bit insane - as the paging toolbar seems to detach the el..
15005 //        dom.parentNode.parentNode.parentNode
15006          // they get detached?
15007     }
15008     
15009     
15010     Roo.View.superclass.constructor.call(this);
15011     
15012     
15013 };
15014
15015 Roo.extend(Roo.View, Roo.util.Observable, {
15016     
15017      /**
15018      * @cfg {Roo.data.Store} store Data store to load data from.
15019      */
15020     store : false,
15021     
15022     /**
15023      * @cfg {String|Roo.Element} el The container element.
15024      */
15025     el : '',
15026     
15027     /**
15028      * @cfg {String|Roo.Template} tpl The template used by this View 
15029      */
15030     tpl : false,
15031     /**
15032      * @cfg {String} dataName the named area of the template to use as the data area
15033      *                          Works with domtemplates roo-name="name"
15034      */
15035     dataName: false,
15036     /**
15037      * @cfg {String} selectedClass The css class to add to selected nodes
15038      */
15039     selectedClass : "x-view-selected",
15040      /**
15041      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15042      */
15043     emptyText : "",
15044     
15045     /**
15046      * @cfg {String} text to display on mask (default Loading)
15047      */
15048     mask : false,
15049     /**
15050      * @cfg {Boolean} multiSelect Allow multiple selection
15051      */
15052     multiSelect : false,
15053     /**
15054      * @cfg {Boolean} singleSelect Allow single selection
15055      */
15056     singleSelect:  false,
15057     
15058     /**
15059      * @cfg {Boolean} toggleSelect - selecting 
15060      */
15061     toggleSelect : false,
15062     
15063     /**
15064      * @cfg {Boolean} tickable - selecting 
15065      */
15066     tickable : false,
15067     
15068     /**
15069      * Returns the element this view is bound to.
15070      * @return {Roo.Element}
15071      */
15072     getEl : function(){
15073         return this.wrapEl;
15074     },
15075     
15076     
15077
15078     /**
15079      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15080      */
15081     refresh : function(){
15082         //Roo.log('refresh');
15083         var t = this.tpl;
15084         
15085         // if we are using something like 'domtemplate', then
15086         // the what gets used is:
15087         // t.applySubtemplate(NAME, data, wrapping data..)
15088         // the outer template then get' applied with
15089         //     the store 'extra data'
15090         // and the body get's added to the
15091         //      roo-name="data" node?
15092         //      <span class='roo-tpl-{name}'></span> ?????
15093         
15094         
15095         
15096         this.clearSelections();
15097         this.el.update("");
15098         var html = [];
15099         var records = this.store.getRange();
15100         if(records.length < 1) {
15101             
15102             // is this valid??  = should it render a template??
15103             
15104             this.el.update(this.emptyText);
15105             return;
15106         }
15107         var el = this.el;
15108         if (this.dataName) {
15109             this.el.update(t.apply(this.store.meta)); //????
15110             el = this.el.child('.roo-tpl-' + this.dataName);
15111         }
15112         
15113         for(var i = 0, len = records.length; i < len; i++){
15114             var data = this.prepareData(records[i].data, i, records[i]);
15115             this.fireEvent("preparedata", this, data, i, records[i]);
15116             
15117             var d = Roo.apply({}, data);
15118             
15119             if(this.tickable){
15120                 Roo.apply(d, {'roo-id' : Roo.id()});
15121                 
15122                 var _this = this;
15123             
15124                 Roo.each(this.parent.item, function(item){
15125                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15126                         return;
15127                     }
15128                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15129                 });
15130             }
15131             
15132             html[html.length] = Roo.util.Format.trim(
15133                 this.dataName ?
15134                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15135                     t.apply(d)
15136             );
15137         }
15138         
15139         
15140         
15141         el.update(html.join(""));
15142         this.nodes = el.dom.childNodes;
15143         this.updateIndexes(0);
15144     },
15145     
15146
15147     /**
15148      * Function to override to reformat the data that is sent to
15149      * the template for each node.
15150      * DEPRICATED - use the preparedata event handler.
15151      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15152      * a JSON object for an UpdateManager bound view).
15153      */
15154     prepareData : function(data, index, record)
15155     {
15156         this.fireEvent("preparedata", this, data, index, record);
15157         return data;
15158     },
15159
15160     onUpdate : function(ds, record){
15161         // Roo.log('on update');   
15162         this.clearSelections();
15163         var index = this.store.indexOf(record);
15164         var n = this.nodes[index];
15165         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15166         n.parentNode.removeChild(n);
15167         this.updateIndexes(index, index);
15168     },
15169
15170     
15171     
15172 // --------- FIXME     
15173     onAdd : function(ds, records, index)
15174     {
15175         //Roo.log(['on Add', ds, records, index] );        
15176         this.clearSelections();
15177         if(this.nodes.length == 0){
15178             this.refresh();
15179             return;
15180         }
15181         var n = this.nodes[index];
15182         for(var i = 0, len = records.length; i < len; i++){
15183             var d = this.prepareData(records[i].data, i, records[i]);
15184             if(n){
15185                 this.tpl.insertBefore(n, d);
15186             }else{
15187                 
15188                 this.tpl.append(this.el, d);
15189             }
15190         }
15191         this.updateIndexes(index);
15192     },
15193
15194     onRemove : function(ds, record, index){
15195        // Roo.log('onRemove');
15196         this.clearSelections();
15197         var el = this.dataName  ?
15198             this.el.child('.roo-tpl-' + this.dataName) :
15199             this.el; 
15200         
15201         el.dom.removeChild(this.nodes[index]);
15202         this.updateIndexes(index);
15203     },
15204
15205     /**
15206      * Refresh an individual node.
15207      * @param {Number} index
15208      */
15209     refreshNode : function(index){
15210         this.onUpdate(this.store, this.store.getAt(index));
15211     },
15212
15213     updateIndexes : function(startIndex, endIndex){
15214         var ns = this.nodes;
15215         startIndex = startIndex || 0;
15216         endIndex = endIndex || ns.length - 1;
15217         for(var i = startIndex; i <= endIndex; i++){
15218             ns[i].nodeIndex = i;
15219         }
15220     },
15221
15222     /**
15223      * Changes the data store this view uses and refresh the view.
15224      * @param {Store} store
15225      */
15226     setStore : function(store, initial){
15227         if(!initial && this.store){
15228             this.store.un("datachanged", this.refresh);
15229             this.store.un("add", this.onAdd);
15230             this.store.un("remove", this.onRemove);
15231             this.store.un("update", this.onUpdate);
15232             this.store.un("clear", this.refresh);
15233             this.store.un("beforeload", this.onBeforeLoad);
15234             this.store.un("load", this.onLoad);
15235             this.store.un("loadexception", this.onLoad);
15236         }
15237         if(store){
15238           
15239             store.on("datachanged", this.refresh, this);
15240             store.on("add", this.onAdd, this);
15241             store.on("remove", this.onRemove, this);
15242             store.on("update", this.onUpdate, this);
15243             store.on("clear", this.refresh, this);
15244             store.on("beforeload", this.onBeforeLoad, this);
15245             store.on("load", this.onLoad, this);
15246             store.on("loadexception", this.onLoad, this);
15247         }
15248         
15249         if(store){
15250             this.refresh();
15251         }
15252     },
15253     /**
15254      * onbeforeLoad - masks the loading area.
15255      *
15256      */
15257     onBeforeLoad : function(store,opts)
15258     {
15259          //Roo.log('onBeforeLoad');   
15260         if (!opts.add) {
15261             this.el.update("");
15262         }
15263         this.el.mask(this.mask ? this.mask : "Loading" ); 
15264     },
15265     onLoad : function ()
15266     {
15267         this.el.unmask();
15268     },
15269     
15270
15271     /**
15272      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15273      * @param {HTMLElement} node
15274      * @return {HTMLElement} The template node
15275      */
15276     findItemFromChild : function(node){
15277         var el = this.dataName  ?
15278             this.el.child('.roo-tpl-' + this.dataName,true) :
15279             this.el.dom; 
15280         
15281         if(!node || node.parentNode == el){
15282                     return node;
15283             }
15284             var p = node.parentNode;
15285             while(p && p != el){
15286             if(p.parentNode == el){
15287                 return p;
15288             }
15289             p = p.parentNode;
15290         }
15291             return null;
15292     },
15293
15294     /** @ignore */
15295     onClick : function(e){
15296         var item = this.findItemFromChild(e.getTarget());
15297         if(item){
15298             var index = this.indexOf(item);
15299             if(this.onItemClick(item, index, e) !== false){
15300                 this.fireEvent("click", this, index, item, e);
15301             }
15302         }else{
15303             this.clearSelections();
15304         }
15305     },
15306
15307     /** @ignore */
15308     onContextMenu : function(e){
15309         var item = this.findItemFromChild(e.getTarget());
15310         if(item){
15311             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15312         }
15313     },
15314
15315     /** @ignore */
15316     onDblClick : function(e){
15317         var item = this.findItemFromChild(e.getTarget());
15318         if(item){
15319             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15320         }
15321     },
15322
15323     onItemClick : function(item, index, e)
15324     {
15325         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15326             return false;
15327         }
15328         if (this.toggleSelect) {
15329             var m = this.isSelected(item) ? 'unselect' : 'select';
15330             //Roo.log(m);
15331             var _t = this;
15332             _t[m](item, true, false);
15333             return true;
15334         }
15335         if(this.multiSelect || this.singleSelect){
15336             if(this.multiSelect && e.shiftKey && this.lastSelection){
15337                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15338             }else{
15339                 this.select(item, this.multiSelect && e.ctrlKey);
15340                 this.lastSelection = item;
15341             }
15342             
15343             if(!this.tickable){
15344                 e.preventDefault();
15345             }
15346             
15347         }
15348         return true;
15349     },
15350
15351     /**
15352      * Get the number of selected nodes.
15353      * @return {Number}
15354      */
15355     getSelectionCount : function(){
15356         return this.selections.length;
15357     },
15358
15359     /**
15360      * Get the currently selected nodes.
15361      * @return {Array} An array of HTMLElements
15362      */
15363     getSelectedNodes : function(){
15364         return this.selections;
15365     },
15366
15367     /**
15368      * Get the indexes of the selected nodes.
15369      * @return {Array}
15370      */
15371     getSelectedIndexes : function(){
15372         var indexes = [], s = this.selections;
15373         for(var i = 0, len = s.length; i < len; i++){
15374             indexes.push(s[i].nodeIndex);
15375         }
15376         return indexes;
15377     },
15378
15379     /**
15380      * Clear all selections
15381      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15382      */
15383     clearSelections : function(suppressEvent){
15384         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15385             this.cmp.elements = this.selections;
15386             this.cmp.removeClass(this.selectedClass);
15387             this.selections = [];
15388             if(!suppressEvent){
15389                 this.fireEvent("selectionchange", this, this.selections);
15390             }
15391         }
15392     },
15393
15394     /**
15395      * Returns true if the passed node is selected
15396      * @param {HTMLElement/Number} node The node or node index
15397      * @return {Boolean}
15398      */
15399     isSelected : function(node){
15400         var s = this.selections;
15401         if(s.length < 1){
15402             return false;
15403         }
15404         node = this.getNode(node);
15405         return s.indexOf(node) !== -1;
15406     },
15407
15408     /**
15409      * Selects nodes.
15410      * @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
15411      * @param {Boolean} keepExisting (optional) true to keep existing selections
15412      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15413      */
15414     select : function(nodeInfo, keepExisting, suppressEvent){
15415         if(nodeInfo instanceof Array){
15416             if(!keepExisting){
15417                 this.clearSelections(true);
15418             }
15419             for(var i = 0, len = nodeInfo.length; i < len; i++){
15420                 this.select(nodeInfo[i], true, true);
15421             }
15422             return;
15423         } 
15424         var node = this.getNode(nodeInfo);
15425         if(!node || this.isSelected(node)){
15426             return; // already selected.
15427         }
15428         if(!keepExisting){
15429             this.clearSelections(true);
15430         }
15431         
15432         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15433             Roo.fly(node).addClass(this.selectedClass);
15434             this.selections.push(node);
15435             if(!suppressEvent){
15436                 this.fireEvent("selectionchange", this, this.selections);
15437             }
15438         }
15439         
15440         
15441     },
15442       /**
15443      * Unselects nodes.
15444      * @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
15445      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15446      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15447      */
15448     unselect : function(nodeInfo, keepExisting, suppressEvent)
15449     {
15450         if(nodeInfo instanceof Array){
15451             Roo.each(this.selections, function(s) {
15452                 this.unselect(s, nodeInfo);
15453             }, this);
15454             return;
15455         }
15456         var node = this.getNode(nodeInfo);
15457         if(!node || !this.isSelected(node)){
15458             //Roo.log("not selected");
15459             return; // not selected.
15460         }
15461         // fireevent???
15462         var ns = [];
15463         Roo.each(this.selections, function(s) {
15464             if (s == node ) {
15465                 Roo.fly(node).removeClass(this.selectedClass);
15466
15467                 return;
15468             }
15469             ns.push(s);
15470         },this);
15471         
15472         this.selections= ns;
15473         this.fireEvent("selectionchange", this, this.selections);
15474     },
15475
15476     /**
15477      * Gets a template node.
15478      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15479      * @return {HTMLElement} The node or null if it wasn't found
15480      */
15481     getNode : function(nodeInfo){
15482         if(typeof nodeInfo == "string"){
15483             return document.getElementById(nodeInfo);
15484         }else if(typeof nodeInfo == "number"){
15485             return this.nodes[nodeInfo];
15486         }
15487         return nodeInfo;
15488     },
15489
15490     /**
15491      * Gets a range template nodes.
15492      * @param {Number} startIndex
15493      * @param {Number} endIndex
15494      * @return {Array} An array of nodes
15495      */
15496     getNodes : function(start, end){
15497         var ns = this.nodes;
15498         start = start || 0;
15499         end = typeof end == "undefined" ? ns.length - 1 : end;
15500         var nodes = [];
15501         if(start <= end){
15502             for(var i = start; i <= end; i++){
15503                 nodes.push(ns[i]);
15504             }
15505         } else{
15506             for(var i = start; i >= end; i--){
15507                 nodes.push(ns[i]);
15508             }
15509         }
15510         return nodes;
15511     },
15512
15513     /**
15514      * Finds the index of the passed node
15515      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15516      * @return {Number} The index of the node or -1
15517      */
15518     indexOf : function(node){
15519         node = this.getNode(node);
15520         if(typeof node.nodeIndex == "number"){
15521             return node.nodeIndex;
15522         }
15523         var ns = this.nodes;
15524         for(var i = 0, len = ns.length; i < len; i++){
15525             if(ns[i] == node){
15526                 return i;
15527             }
15528         }
15529         return -1;
15530     }
15531 });
15532 /*
15533  * - LGPL
15534  *
15535  * based on jquery fullcalendar
15536  * 
15537  */
15538
15539 Roo.bootstrap = Roo.bootstrap || {};
15540 /**
15541  * @class Roo.bootstrap.Calendar
15542  * @extends Roo.bootstrap.Component
15543  * Bootstrap Calendar class
15544  * @cfg {Boolean} loadMask (true|false) default false
15545  * @cfg {Object} header generate the user specific header of the calendar, default false
15546
15547  * @constructor
15548  * Create a new Container
15549  * @param {Object} config The config object
15550  */
15551
15552
15553
15554 Roo.bootstrap.Calendar = function(config){
15555     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15556      this.addEvents({
15557         /**
15558              * @event select
15559              * Fires when a date is selected
15560              * @param {DatePicker} this
15561              * @param {Date} date The selected date
15562              */
15563         'select': true,
15564         /**
15565              * @event monthchange
15566              * Fires when the displayed month changes 
15567              * @param {DatePicker} this
15568              * @param {Date} date The selected month
15569              */
15570         'monthchange': true,
15571         /**
15572              * @event evententer
15573              * Fires when mouse over an event
15574              * @param {Calendar} this
15575              * @param {event} Event
15576              */
15577         'evententer': true,
15578         /**
15579              * @event eventleave
15580              * Fires when the mouse leaves an
15581              * @param {Calendar} this
15582              * @param {event}
15583              */
15584         'eventleave': true,
15585         /**
15586              * @event eventclick
15587              * Fires when the mouse click an
15588              * @param {Calendar} this
15589              * @param {event}
15590              */
15591         'eventclick': true
15592         
15593     });
15594
15595 };
15596
15597 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15598     
15599      /**
15600      * @cfg {Number} startDay
15601      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15602      */
15603     startDay : 0,
15604     
15605     loadMask : false,
15606     
15607     header : false,
15608       
15609     getAutoCreate : function(){
15610         
15611         
15612         var fc_button = function(name, corner, style, content ) {
15613             return Roo.apply({},{
15614                 tag : 'span',
15615                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15616                          (corner.length ?
15617                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15618                             ''
15619                         ),
15620                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15621                 unselectable: 'on'
15622             });
15623         };
15624         
15625         var header = {};
15626         
15627         if(!this.header){
15628             header = {
15629                 tag : 'table',
15630                 cls : 'fc-header',
15631                 style : 'width:100%',
15632                 cn : [
15633                     {
15634                         tag: 'tr',
15635                         cn : [
15636                             {
15637                                 tag : 'td',
15638                                 cls : 'fc-header-left',
15639                                 cn : [
15640                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15641                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15642                                     { tag: 'span', cls: 'fc-header-space' },
15643                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15644
15645
15646                                 ]
15647                             },
15648
15649                             {
15650                                 tag : 'td',
15651                                 cls : 'fc-header-center',
15652                                 cn : [
15653                                     {
15654                                         tag: 'span',
15655                                         cls: 'fc-header-title',
15656                                         cn : {
15657                                             tag: 'H2',
15658                                             html : 'month / year'
15659                                         }
15660                                     }
15661
15662                                 ]
15663                             },
15664                             {
15665                                 tag : 'td',
15666                                 cls : 'fc-header-right',
15667                                 cn : [
15668                               /*      fc_button('month', 'left', '', 'month' ),
15669                                     fc_button('week', '', '', 'week' ),
15670                                     fc_button('day', 'right', '', 'day' )
15671                                 */    
15672
15673                                 ]
15674                             }
15675
15676                         ]
15677                     }
15678                 ]
15679             };
15680         }
15681         
15682         header = this.header;
15683         
15684        
15685         var cal_heads = function() {
15686             var ret = [];
15687             // fixme - handle this.
15688             
15689             for (var i =0; i < Date.dayNames.length; i++) {
15690                 var d = Date.dayNames[i];
15691                 ret.push({
15692                     tag: 'th',
15693                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15694                     html : d.substring(0,3)
15695                 });
15696                 
15697             }
15698             ret[0].cls += ' fc-first';
15699             ret[6].cls += ' fc-last';
15700             return ret;
15701         };
15702         var cal_cell = function(n) {
15703             return  {
15704                 tag: 'td',
15705                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15706                 cn : [
15707                     {
15708                         cn : [
15709                             {
15710                                 cls: 'fc-day-number',
15711                                 html: 'D'
15712                             },
15713                             {
15714                                 cls: 'fc-day-content',
15715                              
15716                                 cn : [
15717                                      {
15718                                         style: 'position: relative;' // height: 17px;
15719                                     }
15720                                 ]
15721                             }
15722                             
15723                             
15724                         ]
15725                     }
15726                 ]
15727                 
15728             }
15729         };
15730         var cal_rows = function() {
15731             
15732             var ret = [];
15733             for (var r = 0; r < 6; r++) {
15734                 var row= {
15735                     tag : 'tr',
15736                     cls : 'fc-week',
15737                     cn : []
15738                 };
15739                 
15740                 for (var i =0; i < Date.dayNames.length; i++) {
15741                     var d = Date.dayNames[i];
15742                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15743
15744                 }
15745                 row.cn[0].cls+=' fc-first';
15746                 row.cn[0].cn[0].style = 'min-height:90px';
15747                 row.cn[6].cls+=' fc-last';
15748                 ret.push(row);
15749                 
15750             }
15751             ret[0].cls += ' fc-first';
15752             ret[4].cls += ' fc-prev-last';
15753             ret[5].cls += ' fc-last';
15754             return ret;
15755             
15756         };
15757         
15758         var cal_table = {
15759             tag: 'table',
15760             cls: 'fc-border-separate',
15761             style : 'width:100%',
15762             cellspacing  : 0,
15763             cn : [
15764                 { 
15765                     tag: 'thead',
15766                     cn : [
15767                         { 
15768                             tag: 'tr',
15769                             cls : 'fc-first fc-last',
15770                             cn : cal_heads()
15771                         }
15772                     ]
15773                 },
15774                 { 
15775                     tag: 'tbody',
15776                     cn : cal_rows()
15777                 }
15778                   
15779             ]
15780         };
15781          
15782          var cfg = {
15783             cls : 'fc fc-ltr',
15784             cn : [
15785                 header,
15786                 {
15787                     cls : 'fc-content',
15788                     style : "position: relative;",
15789                     cn : [
15790                         {
15791                             cls : 'fc-view fc-view-month fc-grid',
15792                             style : 'position: relative',
15793                             unselectable : 'on',
15794                             cn : [
15795                                 {
15796                                     cls : 'fc-event-container',
15797                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15798                                 },
15799                                 cal_table
15800                             ]
15801                         }
15802                     ]
15803     
15804                 }
15805            ] 
15806             
15807         };
15808         
15809          
15810         
15811         return cfg;
15812     },
15813     
15814     
15815     initEvents : function()
15816     {
15817         if(!this.store){
15818             throw "can not find store for calendar";
15819         }
15820         
15821         var mark = {
15822             tag: "div",
15823             cls:"x-dlg-mask",
15824             style: "text-align:center",
15825             cn: [
15826                 {
15827                     tag: "div",
15828                     style: "background-color:white;width:50%;margin:250 auto",
15829                     cn: [
15830                         {
15831                             tag: "img",
15832                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15833                         },
15834                         {
15835                             tag: "span",
15836                             html: "Loading"
15837                         }
15838                         
15839                     ]
15840                 }
15841             ]
15842         };
15843         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15844         
15845         var size = this.el.select('.fc-content', true).first().getSize();
15846         this.maskEl.setSize(size.width, size.height);
15847         this.maskEl.enableDisplayMode("block");
15848         if(!this.loadMask){
15849             this.maskEl.hide();
15850         }
15851         
15852         this.store = Roo.factory(this.store, Roo.data);
15853         this.store.on('load', this.onLoad, this);
15854         this.store.on('beforeload', this.onBeforeLoad, this);
15855         
15856         this.resize();
15857         
15858         this.cells = this.el.select('.fc-day',true);
15859         //Roo.log(this.cells);
15860         this.textNodes = this.el.query('.fc-day-number');
15861         this.cells.addClassOnOver('fc-state-hover');
15862         
15863         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15864         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15865         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15866         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15867         
15868         this.on('monthchange', this.onMonthChange, this);
15869         
15870         this.update(new Date().clearTime());
15871     },
15872     
15873     resize : function() {
15874         var sz  = this.el.getSize();
15875         
15876         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15877         this.el.select('.fc-day-content div',true).setHeight(34);
15878     },
15879     
15880     
15881     // private
15882     showPrevMonth : function(e){
15883         this.update(this.activeDate.add("mo", -1));
15884     },
15885     showToday : function(e){
15886         this.update(new Date().clearTime());
15887     },
15888     // private
15889     showNextMonth : function(e){
15890         this.update(this.activeDate.add("mo", 1));
15891     },
15892
15893     // private
15894     showPrevYear : function(){
15895         this.update(this.activeDate.add("y", -1));
15896     },
15897
15898     // private
15899     showNextYear : function(){
15900         this.update(this.activeDate.add("y", 1));
15901     },
15902
15903     
15904    // private
15905     update : function(date)
15906     {
15907         var vd = this.activeDate;
15908         this.activeDate = date;
15909 //        if(vd && this.el){
15910 //            var t = date.getTime();
15911 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15912 //                Roo.log('using add remove');
15913 //                
15914 //                this.fireEvent('monthchange', this, date);
15915 //                
15916 //                this.cells.removeClass("fc-state-highlight");
15917 //                this.cells.each(function(c){
15918 //                   if(c.dateValue == t){
15919 //                       c.addClass("fc-state-highlight");
15920 //                       setTimeout(function(){
15921 //                            try{c.dom.firstChild.focus();}catch(e){}
15922 //                       }, 50);
15923 //                       return false;
15924 //                   }
15925 //                   return true;
15926 //                });
15927 //                return;
15928 //            }
15929 //        }
15930         
15931         var days = date.getDaysInMonth();
15932         
15933         var firstOfMonth = date.getFirstDateOfMonth();
15934         var startingPos = firstOfMonth.getDay()-this.startDay;
15935         
15936         if(startingPos < this.startDay){
15937             startingPos += 7;
15938         }
15939         
15940         var pm = date.add(Date.MONTH, -1);
15941         var prevStart = pm.getDaysInMonth()-startingPos;
15942 //        
15943         this.cells = this.el.select('.fc-day',true);
15944         this.textNodes = this.el.query('.fc-day-number');
15945         this.cells.addClassOnOver('fc-state-hover');
15946         
15947         var cells = this.cells.elements;
15948         var textEls = this.textNodes;
15949         
15950         Roo.each(cells, function(cell){
15951             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15952         });
15953         
15954         days += startingPos;
15955
15956         // convert everything to numbers so it's fast
15957         var day = 86400000;
15958         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15959         //Roo.log(d);
15960         //Roo.log(pm);
15961         //Roo.log(prevStart);
15962         
15963         var today = new Date().clearTime().getTime();
15964         var sel = date.clearTime().getTime();
15965         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15966         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15967         var ddMatch = this.disabledDatesRE;
15968         var ddText = this.disabledDatesText;
15969         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15970         var ddaysText = this.disabledDaysText;
15971         var format = this.format;
15972         
15973         var setCellClass = function(cal, cell){
15974             cell.row = 0;
15975             cell.events = [];
15976             cell.more = [];
15977             //Roo.log('set Cell Class');
15978             cell.title = "";
15979             var t = d.getTime();
15980             
15981             //Roo.log(d);
15982             
15983             cell.dateValue = t;
15984             if(t == today){
15985                 cell.className += " fc-today";
15986                 cell.className += " fc-state-highlight";
15987                 cell.title = cal.todayText;
15988             }
15989             if(t == sel){
15990                 // disable highlight in other month..
15991                 //cell.className += " fc-state-highlight";
15992                 
15993             }
15994             // disabling
15995             if(t < min) {
15996                 cell.className = " fc-state-disabled";
15997                 cell.title = cal.minText;
15998                 return;
15999             }
16000             if(t > max) {
16001                 cell.className = " fc-state-disabled";
16002                 cell.title = cal.maxText;
16003                 return;
16004             }
16005             if(ddays){
16006                 if(ddays.indexOf(d.getDay()) != -1){
16007                     cell.title = ddaysText;
16008                     cell.className = " fc-state-disabled";
16009                 }
16010             }
16011             if(ddMatch && format){
16012                 var fvalue = d.dateFormat(format);
16013                 if(ddMatch.test(fvalue)){
16014                     cell.title = ddText.replace("%0", fvalue);
16015                     cell.className = " fc-state-disabled";
16016                 }
16017             }
16018             
16019             if (!cell.initialClassName) {
16020                 cell.initialClassName = cell.dom.className;
16021             }
16022             
16023             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16024         };
16025
16026         var i = 0;
16027         
16028         for(; i < startingPos; i++) {
16029             textEls[i].innerHTML = (++prevStart);
16030             d.setDate(d.getDate()+1);
16031             
16032             cells[i].className = "fc-past fc-other-month";
16033             setCellClass(this, cells[i]);
16034         }
16035         
16036         var intDay = 0;
16037         
16038         for(; i < days; i++){
16039             intDay = i - startingPos + 1;
16040             textEls[i].innerHTML = (intDay);
16041             d.setDate(d.getDate()+1);
16042             
16043             cells[i].className = ''; // "x-date-active";
16044             setCellClass(this, cells[i]);
16045         }
16046         var extraDays = 0;
16047         
16048         for(; i < 42; i++) {
16049             textEls[i].innerHTML = (++extraDays);
16050             d.setDate(d.getDate()+1);
16051             
16052             cells[i].className = "fc-future fc-other-month";
16053             setCellClass(this, cells[i]);
16054         }
16055         
16056         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16057         
16058         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16059         
16060         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16061         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16062         
16063         if(totalRows != 6){
16064             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16065             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16066         }
16067         
16068         this.fireEvent('monthchange', this, date);
16069         
16070         
16071         /*
16072         if(!this.internalRender){
16073             var main = this.el.dom.firstChild;
16074             var w = main.offsetWidth;
16075             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16076             Roo.fly(main).setWidth(w);
16077             this.internalRender = true;
16078             // opera does not respect the auto grow header center column
16079             // then, after it gets a width opera refuses to recalculate
16080             // without a second pass
16081             if(Roo.isOpera && !this.secondPass){
16082                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16083                 this.secondPass = true;
16084                 this.update.defer(10, this, [date]);
16085             }
16086         }
16087         */
16088         
16089     },
16090     
16091     findCell : function(dt) {
16092         dt = dt.clearTime().getTime();
16093         var ret = false;
16094         this.cells.each(function(c){
16095             //Roo.log("check " +c.dateValue + '?=' + dt);
16096             if(c.dateValue == dt){
16097                 ret = c;
16098                 return false;
16099             }
16100             return true;
16101         });
16102         
16103         return ret;
16104     },
16105     
16106     findCells : function(ev) {
16107         var s = ev.start.clone().clearTime().getTime();
16108        // Roo.log(s);
16109         var e= ev.end.clone().clearTime().getTime();
16110        // Roo.log(e);
16111         var ret = [];
16112         this.cells.each(function(c){
16113              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16114             
16115             if(c.dateValue > e){
16116                 return ;
16117             }
16118             if(c.dateValue < s){
16119                 return ;
16120             }
16121             ret.push(c);
16122         });
16123         
16124         return ret;    
16125     },
16126     
16127 //    findBestRow: function(cells)
16128 //    {
16129 //        var ret = 0;
16130 //        
16131 //        for (var i =0 ; i < cells.length;i++) {
16132 //            ret  = Math.max(cells[i].rows || 0,ret);
16133 //        }
16134 //        return ret;
16135 //        
16136 //    },
16137     
16138     
16139     addItem : function(ev)
16140     {
16141         // look for vertical location slot in
16142         var cells = this.findCells(ev);
16143         
16144 //        ev.row = this.findBestRow(cells);
16145         
16146         // work out the location.
16147         
16148         var crow = false;
16149         var rows = [];
16150         for(var i =0; i < cells.length; i++) {
16151             
16152             cells[i].row = cells[0].row;
16153             
16154             if(i == 0){
16155                 cells[i].row = cells[i].row + 1;
16156             }
16157             
16158             if (!crow) {
16159                 crow = {
16160                     start : cells[i],
16161                     end :  cells[i]
16162                 };
16163                 continue;
16164             }
16165             if (crow.start.getY() == cells[i].getY()) {
16166                 // on same row.
16167                 crow.end = cells[i];
16168                 continue;
16169             }
16170             // different row.
16171             rows.push(crow);
16172             crow = {
16173                 start: cells[i],
16174                 end : cells[i]
16175             };
16176             
16177         }
16178         
16179         rows.push(crow);
16180         ev.els = [];
16181         ev.rows = rows;
16182         ev.cells = cells;
16183         
16184         cells[0].events.push(ev);
16185         
16186         this.calevents.push(ev);
16187     },
16188     
16189     clearEvents: function() {
16190         
16191         if(!this.calevents){
16192             return;
16193         }
16194         
16195         Roo.each(this.cells.elements, function(c){
16196             c.row = 0;
16197             c.events = [];
16198             c.more = [];
16199         });
16200         
16201         Roo.each(this.calevents, function(e) {
16202             Roo.each(e.els, function(el) {
16203                 el.un('mouseenter' ,this.onEventEnter, this);
16204                 el.un('mouseleave' ,this.onEventLeave, this);
16205                 el.remove();
16206             },this);
16207         },this);
16208         
16209         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16210             e.remove();
16211         });
16212         
16213     },
16214     
16215     renderEvents: function()
16216     {   
16217         var _this = this;
16218         
16219         this.cells.each(function(c) {
16220             
16221             if(c.row < 5){
16222                 return;
16223             }
16224             
16225             var ev = c.events;
16226             
16227             var r = 4;
16228             if(c.row != c.events.length){
16229                 r = 4 - (4 - (c.row - c.events.length));
16230             }
16231             
16232             c.events = ev.slice(0, r);
16233             c.more = ev.slice(r);
16234             
16235             if(c.more.length && c.more.length == 1){
16236                 c.events.push(c.more.pop());
16237             }
16238             
16239             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16240             
16241         });
16242             
16243         this.cells.each(function(c) {
16244             
16245             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16246             
16247             
16248             for (var e = 0; e < c.events.length; e++){
16249                 var ev = c.events[e];
16250                 var rows = ev.rows;
16251                 
16252                 for(var i = 0; i < rows.length; i++) {
16253                 
16254                     // how many rows should it span..
16255
16256                     var  cfg = {
16257                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16258                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16259
16260                         unselectable : "on",
16261                         cn : [
16262                             {
16263                                 cls: 'fc-event-inner',
16264                                 cn : [
16265     //                                {
16266     //                                  tag:'span',
16267     //                                  cls: 'fc-event-time',
16268     //                                  html : cells.length > 1 ? '' : ev.time
16269     //                                },
16270                                     {
16271                                       tag:'span',
16272                                       cls: 'fc-event-title',
16273                                       html : String.format('{0}', ev.title)
16274                                     }
16275
16276
16277                                 ]
16278                             },
16279                             {
16280                                 cls: 'ui-resizable-handle ui-resizable-e',
16281                                 html : '&nbsp;&nbsp;&nbsp'
16282                             }
16283
16284                         ]
16285                     };
16286
16287                     if (i == 0) {
16288                         cfg.cls += ' fc-event-start';
16289                     }
16290                     if ((i+1) == rows.length) {
16291                         cfg.cls += ' fc-event-end';
16292                     }
16293
16294                     var ctr = _this.el.select('.fc-event-container',true).first();
16295                     var cg = ctr.createChild(cfg);
16296
16297                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16298                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16299
16300                     var r = (c.more.length) ? 1 : 0;
16301                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16302                     cg.setWidth(ebox.right - sbox.x -2);
16303
16304                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16305                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16306                     cg.on('click', _this.onEventClick, _this, ev);
16307
16308                     ev.els.push(cg);
16309                     
16310                 }
16311                 
16312             }
16313             
16314             
16315             if(c.more.length){
16316                 var  cfg = {
16317                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16318                     style : 'position: absolute',
16319                     unselectable : "on",
16320                     cn : [
16321                         {
16322                             cls: 'fc-event-inner',
16323                             cn : [
16324                                 {
16325                                   tag:'span',
16326                                   cls: 'fc-event-title',
16327                                   html : 'More'
16328                                 }
16329
16330
16331                             ]
16332                         },
16333                         {
16334                             cls: 'ui-resizable-handle ui-resizable-e',
16335                             html : '&nbsp;&nbsp;&nbsp'
16336                         }
16337
16338                     ]
16339                 };
16340
16341                 var ctr = _this.el.select('.fc-event-container',true).first();
16342                 var cg = ctr.createChild(cfg);
16343
16344                 var sbox = c.select('.fc-day-content',true).first().getBox();
16345                 var ebox = c.select('.fc-day-content',true).first().getBox();
16346                 //Roo.log(cg);
16347                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16348                 cg.setWidth(ebox.right - sbox.x -2);
16349
16350                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16351                 
16352             }
16353             
16354         });
16355         
16356         
16357         
16358     },
16359     
16360     onEventEnter: function (e, el,event,d) {
16361         this.fireEvent('evententer', this, el, event);
16362     },
16363     
16364     onEventLeave: function (e, el,event,d) {
16365         this.fireEvent('eventleave', this, el, event);
16366     },
16367     
16368     onEventClick: function (e, el,event,d) {
16369         this.fireEvent('eventclick', this, el, event);
16370     },
16371     
16372     onMonthChange: function () {
16373         this.store.load();
16374     },
16375     
16376     onMoreEventClick: function(e, el, more)
16377     {
16378         var _this = this;
16379         
16380         this.calpopover.placement = 'right';
16381         this.calpopover.setTitle('More');
16382         
16383         this.calpopover.setContent('');
16384         
16385         var ctr = this.calpopover.el.select('.popover-content', true).first();
16386         
16387         Roo.each(more, function(m){
16388             var cfg = {
16389                 cls : 'fc-event-hori fc-event-draggable',
16390                 html : m.title
16391             };
16392             var cg = ctr.createChild(cfg);
16393             
16394             cg.on('click', _this.onEventClick, _this, m);
16395         });
16396         
16397         this.calpopover.show(el);
16398         
16399         
16400     },
16401     
16402     onLoad: function () 
16403     {   
16404         this.calevents = [];
16405         var cal = this;
16406         
16407         if(this.store.getCount() > 0){
16408             this.store.data.each(function(d){
16409                cal.addItem({
16410                     id : d.data.id,
16411                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16412                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16413                     time : d.data.start_time,
16414                     title : d.data.title,
16415                     description : d.data.description,
16416                     venue : d.data.venue
16417                 });
16418             });
16419         }
16420         
16421         this.renderEvents();
16422         
16423         if(this.calevents.length && this.loadMask){
16424             this.maskEl.hide();
16425         }
16426     },
16427     
16428     onBeforeLoad: function()
16429     {
16430         this.clearEvents();
16431         if(this.loadMask){
16432             this.maskEl.show();
16433         }
16434     }
16435 });
16436
16437  
16438  /*
16439  * - LGPL
16440  *
16441  * element
16442  * 
16443  */
16444
16445 /**
16446  * @class Roo.bootstrap.Popover
16447  * @extends Roo.bootstrap.Component
16448  * Bootstrap Popover class
16449  * @cfg {String} html contents of the popover   (or false to use children..)
16450  * @cfg {String} title of popover (or false to hide)
16451  * @cfg {String} placement how it is placed
16452  * @cfg {String} trigger click || hover (or false to trigger manually)
16453  * @cfg {String} over what (parent or false to trigger manually.)
16454  * @cfg {Number} delay - delay before showing
16455  
16456  * @constructor
16457  * Create a new Popover
16458  * @param {Object} config The config object
16459  */
16460
16461 Roo.bootstrap.Popover = function(config){
16462     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16463     
16464     this.addEvents({
16465         // raw events
16466          /**
16467          * @event show
16468          * After the popover show
16469          * 
16470          * @param {Roo.bootstrap.Popover} this
16471          */
16472         "show" : true,
16473         /**
16474          * @event hide
16475          * After the popover hide
16476          * 
16477          * @param {Roo.bootstrap.Popover} this
16478          */
16479         "hide" : true
16480     });
16481 };
16482
16483 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16484     
16485     title: 'Fill in a title',
16486     html: false,
16487     
16488     placement : 'right',
16489     trigger : 'hover', // hover
16490     
16491     delay : 0,
16492     
16493     over: 'parent',
16494     
16495     can_build_overlaid : false,
16496     
16497     getChildContainer : function()
16498     {
16499         return this.el.select('.popover-content',true).first();
16500     },
16501     
16502     getAutoCreate : function(){
16503          
16504         var cfg = {
16505            cls : 'popover roo-dynamic',
16506            style: 'display:block',
16507            cn : [
16508                 {
16509                     cls : 'arrow'
16510                 },
16511                 {
16512                     cls : 'popover-inner',
16513                     cn : [
16514                         {
16515                             tag: 'h3',
16516                             cls: 'popover-title',
16517                             html : this.title
16518                         },
16519                         {
16520                             cls : 'popover-content',
16521                             html : this.html
16522                         }
16523                     ]
16524                     
16525                 }
16526            ]
16527         };
16528         
16529         return cfg;
16530     },
16531     setTitle: function(str)
16532     {
16533         this.title = str;
16534         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16535     },
16536     setContent: function(str)
16537     {
16538         this.html = str;
16539         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16540     },
16541     // as it get's added to the bottom of the page.
16542     onRender : function(ct, position)
16543     {
16544         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16545         if(!this.el){
16546             var cfg = Roo.apply({},  this.getAutoCreate());
16547             cfg.id = Roo.id();
16548             
16549             if (this.cls) {
16550                 cfg.cls += ' ' + this.cls;
16551             }
16552             if (this.style) {
16553                 cfg.style = this.style;
16554             }
16555             //Roo.log("adding to ");
16556             this.el = Roo.get(document.body).createChild(cfg, position);
16557 //            Roo.log(this.el);
16558         }
16559         this.initEvents();
16560     },
16561     
16562     initEvents : function()
16563     {
16564         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16565         this.el.enableDisplayMode('block');
16566         this.el.hide();
16567         if (this.over === false) {
16568             return; 
16569         }
16570         if (this.triggers === false) {
16571             return;
16572         }
16573         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16574         var triggers = this.trigger ? this.trigger.split(' ') : [];
16575         Roo.each(triggers, function(trigger) {
16576         
16577             if (trigger == 'click') {
16578                 on_el.on('click', this.toggle, this);
16579             } else if (trigger != 'manual') {
16580                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16581                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16582       
16583                 on_el.on(eventIn  ,this.enter, this);
16584                 on_el.on(eventOut, this.leave, this);
16585             }
16586         }, this);
16587         
16588     },
16589     
16590     
16591     // private
16592     timeout : null,
16593     hoverState : null,
16594     
16595     toggle : function () {
16596         this.hoverState == 'in' ? this.leave() : this.enter();
16597     },
16598     
16599     enter : function () {
16600         
16601         clearTimeout(this.timeout);
16602     
16603         this.hoverState = 'in';
16604     
16605         if (!this.delay || !this.delay.show) {
16606             this.show();
16607             return;
16608         }
16609         var _t = this;
16610         this.timeout = setTimeout(function () {
16611             if (_t.hoverState == 'in') {
16612                 _t.show();
16613             }
16614         }, this.delay.show)
16615     },
16616     
16617     leave : function() {
16618         clearTimeout(this.timeout);
16619     
16620         this.hoverState = 'out';
16621     
16622         if (!this.delay || !this.delay.hide) {
16623             this.hide();
16624             return;
16625         }
16626         var _t = this;
16627         this.timeout = setTimeout(function () {
16628             if (_t.hoverState == 'out') {
16629                 _t.hide();
16630             }
16631         }, this.delay.hide)
16632     },
16633     
16634     show : function (on_el)
16635     {
16636         if (!on_el) {
16637             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16638         }
16639         
16640         // set content.
16641         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16642         if (this.html !== false) {
16643             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16644         }
16645         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16646         if (!this.title.length) {
16647             this.el.select('.popover-title',true).hide();
16648         }
16649         
16650         var placement = typeof this.placement == 'function' ?
16651             this.placement.call(this, this.el, on_el) :
16652             this.placement;
16653             
16654         var autoToken = /\s?auto?\s?/i;
16655         var autoPlace = autoToken.test(placement);
16656         if (autoPlace) {
16657             placement = placement.replace(autoToken, '') || 'top';
16658         }
16659         
16660         //this.el.detach()
16661         //this.el.setXY([0,0]);
16662         this.el.show();
16663         this.el.dom.style.display='block';
16664         this.el.addClass(placement);
16665         
16666         //this.el.appendTo(on_el);
16667         
16668         var p = this.getPosition();
16669         var box = this.el.getBox();
16670         
16671         if (autoPlace) {
16672             // fixme..
16673         }
16674         var align = Roo.bootstrap.Popover.alignment[placement];
16675         this.el.alignTo(on_el, align[0],align[1]);
16676         //var arrow = this.el.select('.arrow',true).first();
16677         //arrow.set(align[2], 
16678         
16679         this.el.addClass('in');
16680         
16681         
16682         if (this.el.hasClass('fade')) {
16683             // fade it?
16684         }
16685         
16686         this.hoverState = 'in';
16687         
16688         this.fireEvent('show', this);
16689         
16690     },
16691     hide : function()
16692     {
16693         this.el.setXY([0,0]);
16694         this.el.removeClass('in');
16695         this.el.hide();
16696         this.hoverState = null;
16697         
16698         this.fireEvent('hide', this);
16699     }
16700     
16701 });
16702
16703 Roo.bootstrap.Popover.alignment = {
16704     'left' : ['r-l', [-10,0], 'right'],
16705     'right' : ['l-r', [10,0], 'left'],
16706     'bottom' : ['t-b', [0,10], 'top'],
16707     'top' : [ 'b-t', [0,-10], 'bottom']
16708 };
16709
16710  /*
16711  * - LGPL
16712  *
16713  * Progress
16714  * 
16715  */
16716
16717 /**
16718  * @class Roo.bootstrap.Progress
16719  * @extends Roo.bootstrap.Component
16720  * Bootstrap Progress class
16721  * @cfg {Boolean} striped striped of the progress bar
16722  * @cfg {Boolean} active animated of the progress bar
16723  * 
16724  * 
16725  * @constructor
16726  * Create a new Progress
16727  * @param {Object} config The config object
16728  */
16729
16730 Roo.bootstrap.Progress = function(config){
16731     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16732 };
16733
16734 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16735     
16736     striped : false,
16737     active: false,
16738     
16739     getAutoCreate : function(){
16740         var cfg = {
16741             tag: 'div',
16742             cls: 'progress'
16743         };
16744         
16745         
16746         if(this.striped){
16747             cfg.cls += ' progress-striped';
16748         }
16749       
16750         if(this.active){
16751             cfg.cls += ' active';
16752         }
16753         
16754         
16755         return cfg;
16756     }
16757    
16758 });
16759
16760  
16761
16762  /*
16763  * - LGPL
16764  *
16765  * ProgressBar
16766  * 
16767  */
16768
16769 /**
16770  * @class Roo.bootstrap.ProgressBar
16771  * @extends Roo.bootstrap.Component
16772  * Bootstrap ProgressBar class
16773  * @cfg {Number} aria_valuenow aria-value now
16774  * @cfg {Number} aria_valuemin aria-value min
16775  * @cfg {Number} aria_valuemax aria-value max
16776  * @cfg {String} label label for the progress bar
16777  * @cfg {String} panel (success | info | warning | danger )
16778  * @cfg {String} role role of the progress bar
16779  * @cfg {String} sr_only text
16780  * 
16781  * 
16782  * @constructor
16783  * Create a new ProgressBar
16784  * @param {Object} config The config object
16785  */
16786
16787 Roo.bootstrap.ProgressBar = function(config){
16788     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16789 };
16790
16791 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16792     
16793     aria_valuenow : 0,
16794     aria_valuemin : 0,
16795     aria_valuemax : 100,
16796     label : false,
16797     panel : false,
16798     role : false,
16799     sr_only: false,
16800     
16801     getAutoCreate : function()
16802     {
16803         
16804         var cfg = {
16805             tag: 'div',
16806             cls: 'progress-bar',
16807             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16808         };
16809         
16810         if(this.sr_only){
16811             cfg.cn = {
16812                 tag: 'span',
16813                 cls: 'sr-only',
16814                 html: this.sr_only
16815             }
16816         }
16817         
16818         if(this.role){
16819             cfg.role = this.role;
16820         }
16821         
16822         if(this.aria_valuenow){
16823             cfg['aria-valuenow'] = this.aria_valuenow;
16824         }
16825         
16826         if(this.aria_valuemin){
16827             cfg['aria-valuemin'] = this.aria_valuemin;
16828         }
16829         
16830         if(this.aria_valuemax){
16831             cfg['aria-valuemax'] = this.aria_valuemax;
16832         }
16833         
16834         if(this.label && !this.sr_only){
16835             cfg.html = this.label;
16836         }
16837         
16838         if(this.panel){
16839             cfg.cls += ' progress-bar-' + this.panel;
16840         }
16841         
16842         return cfg;
16843     },
16844     
16845     update : function(aria_valuenow)
16846     {
16847         this.aria_valuenow = aria_valuenow;
16848         
16849         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16850     }
16851    
16852 });
16853
16854  
16855
16856  /*
16857  * - LGPL
16858  *
16859  * column
16860  * 
16861  */
16862
16863 /**
16864  * @class Roo.bootstrap.TabGroup
16865  * @extends Roo.bootstrap.Column
16866  * Bootstrap Column class
16867  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16868  * @cfg {Boolean} carousel true to make the group behave like a carousel
16869  * @cfg {Boolean} bullets show bullets for the panels
16870  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16871  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16872  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16873  * @cfg {Boolean} showarrow (true|false) show arrow default true
16874  * 
16875  * @constructor
16876  * Create a new TabGroup
16877  * @param {Object} config The config object
16878  */
16879
16880 Roo.bootstrap.TabGroup = function(config){
16881     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16882     if (!this.navId) {
16883         this.navId = Roo.id();
16884     }
16885     this.tabs = [];
16886     Roo.bootstrap.TabGroup.register(this);
16887     
16888 };
16889
16890 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16891     
16892     carousel : false,
16893     transition : false,
16894     bullets : 0,
16895     timer : 0,
16896     autoslide : false,
16897     slideFn : false,
16898     slideOnTouch : false,
16899     showarrow : true,
16900     
16901     getAutoCreate : function()
16902     {
16903         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16904         
16905         cfg.cls += ' tab-content';
16906         
16907         if (this.carousel) {
16908             cfg.cls += ' carousel slide';
16909             
16910             cfg.cn = [{
16911                cls : 'carousel-inner',
16912                cn : []
16913             }];
16914         
16915             if(this.bullets  && !Roo.isTouch){
16916                 
16917                 var bullets = {
16918                     cls : 'carousel-bullets',
16919                     cn : []
16920                 };
16921                
16922                 if(this.bullets_cls){
16923                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16924                 }
16925                 
16926                 bullets.cn.push({
16927                     cls : 'clear'
16928                 });
16929                 
16930                 cfg.cn[0].cn.push(bullets);
16931             }
16932             
16933             if(this.showarrow){
16934                 cfg.cn[0].cn.push({
16935                     tag : 'div',
16936                     class : 'carousel-arrow',
16937                     cn : [
16938                         {
16939                             tag : 'div',
16940                             class : 'carousel-prev',
16941                             cn : [
16942                                 {
16943                                     tag : 'i',
16944                                     class : 'fa fa-chevron-left'
16945                                 }
16946                             ]
16947                         },
16948                         {
16949                             tag : 'div',
16950                             class : 'carousel-next',
16951                             cn : [
16952                                 {
16953                                     tag : 'i',
16954                                     class : 'fa fa-chevron-right'
16955                                 }
16956                             ]
16957                         }
16958                     ]
16959                 });
16960             }
16961             
16962         }
16963         
16964         return cfg;
16965     },
16966     
16967     initEvents:  function()
16968     {
16969         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16970             this.el.on("touchstart", this.onTouchStart, this);
16971         }
16972         
16973         if(this.autoslide){
16974             var _this = this;
16975             
16976             this.slideFn = window.setInterval(function() {
16977                 _this.showPanelNext();
16978             }, this.timer);
16979         }
16980         
16981         if(this.showarrow){
16982             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16983             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16984         }
16985         
16986         
16987     },
16988     
16989     onTouchStart : function(e, el, o)
16990     {
16991         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16992             return;
16993         }
16994         
16995         this.showPanelNext();
16996     },
16997     
16998     getChildContainer : function()
16999     {
17000         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17001     },
17002     
17003     /**
17004     * register a Navigation item
17005     * @param {Roo.bootstrap.NavItem} the navitem to add
17006     */
17007     register : function(item)
17008     {
17009         this.tabs.push( item);
17010         item.navId = this.navId; // not really needed..
17011         this.addBullet();
17012     
17013     },
17014     
17015     getActivePanel : function()
17016     {
17017         var r = false;
17018         Roo.each(this.tabs, function(t) {
17019             if (t.active) {
17020                 r = t;
17021                 return false;
17022             }
17023             return null;
17024         });
17025         return r;
17026         
17027     },
17028     getPanelByName : function(n)
17029     {
17030         var r = false;
17031         Roo.each(this.tabs, function(t) {
17032             if (t.tabId == n) {
17033                 r = t;
17034                 return false;
17035             }
17036             return null;
17037         });
17038         return r;
17039     },
17040     indexOfPanel : function(p)
17041     {
17042         var r = false;
17043         Roo.each(this.tabs, function(t,i) {
17044             if (t.tabId == p.tabId) {
17045                 r = i;
17046                 return false;
17047             }
17048             return null;
17049         });
17050         return r;
17051     },
17052     /**
17053      * show a specific panel
17054      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17055      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17056      */
17057     showPanel : function (pan)
17058     {
17059         if(this.transition || typeof(pan) == 'undefined'){
17060             Roo.log("waiting for the transitionend");
17061             return;
17062         }
17063         
17064         if (typeof(pan) == 'number') {
17065             pan = this.tabs[pan];
17066         }
17067         
17068         if (typeof(pan) == 'string') {
17069             pan = this.getPanelByName(pan);
17070         }
17071         
17072         var cur = this.getActivePanel();
17073         
17074         if(!pan || !cur){
17075             Roo.log('pan or acitve pan is undefined');
17076             return false;
17077         }
17078         
17079         if (pan.tabId == this.getActivePanel().tabId) {
17080             return true;
17081         }
17082         
17083         if (false === cur.fireEvent('beforedeactivate')) {
17084             return false;
17085         }
17086         
17087         if(this.bullets > 0 && !Roo.isTouch){
17088             this.setActiveBullet(this.indexOfPanel(pan));
17089         }
17090         
17091         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17092             
17093             this.transition = true;
17094             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17095             var lr = dir == 'next' ? 'left' : 'right';
17096             pan.el.addClass(dir); // or prev
17097             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17098             cur.el.addClass(lr); // or right
17099             pan.el.addClass(lr);
17100             
17101             var _this = this;
17102             cur.el.on('transitionend', function() {
17103                 Roo.log("trans end?");
17104                 
17105                 pan.el.removeClass([lr,dir]);
17106                 pan.setActive(true);
17107                 
17108                 cur.el.removeClass([lr]);
17109                 cur.setActive(false);
17110                 
17111                 _this.transition = false;
17112                 
17113             }, this, { single:  true } );
17114             
17115             return true;
17116         }
17117         
17118         cur.setActive(false);
17119         pan.setActive(true);
17120         
17121         return true;
17122         
17123     },
17124     showPanelNext : function()
17125     {
17126         var i = this.indexOfPanel(this.getActivePanel());
17127         
17128         if (i >= this.tabs.length - 1 && !this.autoslide) {
17129             return;
17130         }
17131         
17132         if (i >= this.tabs.length - 1 && this.autoslide) {
17133             i = -1;
17134         }
17135         
17136         this.showPanel(this.tabs[i+1]);
17137     },
17138     
17139     showPanelPrev : function()
17140     {
17141         var i = this.indexOfPanel(this.getActivePanel());
17142         
17143         if (i  < 1 && !this.autoslide) {
17144             return;
17145         }
17146         
17147         if (i < 1 && this.autoslide) {
17148             i = this.tabs.length;
17149         }
17150         
17151         this.showPanel(this.tabs[i-1]);
17152     },
17153     
17154     
17155     addBullet: function()
17156     {
17157         if(!this.bullets || Roo.isTouch){
17158             return;
17159         }
17160         var ctr = this.el.select('.carousel-bullets',true).first();
17161         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17162         var bullet = ctr.createChild({
17163             cls : 'bullet bullet-' + i
17164         },ctr.dom.lastChild);
17165         
17166         
17167         var _this = this;
17168         
17169         bullet.on('click', (function(e, el, o, ii, t){
17170
17171             e.preventDefault();
17172
17173             this.showPanel(ii);
17174
17175             if(this.autoslide && this.slideFn){
17176                 clearInterval(this.slideFn);
17177                 this.slideFn = window.setInterval(function() {
17178                     _this.showPanelNext();
17179                 }, this.timer);
17180             }
17181
17182         }).createDelegate(this, [i, bullet], true));
17183                 
17184         
17185     },
17186      
17187     setActiveBullet : function(i)
17188     {
17189         if(Roo.isTouch){
17190             return;
17191         }
17192         
17193         Roo.each(this.el.select('.bullet', true).elements, function(el){
17194             el.removeClass('selected');
17195         });
17196
17197         var bullet = this.el.select('.bullet-' + i, true).first();
17198         
17199         if(!bullet){
17200             return;
17201         }
17202         
17203         bullet.addClass('selected');
17204     }
17205     
17206     
17207   
17208 });
17209
17210  
17211
17212  
17213  
17214 Roo.apply(Roo.bootstrap.TabGroup, {
17215     
17216     groups: {},
17217      /**
17218     * register a Navigation Group
17219     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17220     */
17221     register : function(navgrp)
17222     {
17223         this.groups[navgrp.navId] = navgrp;
17224         
17225     },
17226     /**
17227     * fetch a Navigation Group based on the navigation ID
17228     * if one does not exist , it will get created.
17229     * @param {string} the navgroup to add
17230     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17231     */
17232     get: function(navId) {
17233         if (typeof(this.groups[navId]) == 'undefined') {
17234             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17235         }
17236         return this.groups[navId] ;
17237     }
17238     
17239     
17240     
17241 });
17242
17243  /*
17244  * - LGPL
17245  *
17246  * TabPanel
17247  * 
17248  */
17249
17250 /**
17251  * @class Roo.bootstrap.TabPanel
17252  * @extends Roo.bootstrap.Component
17253  * Bootstrap TabPanel class
17254  * @cfg {Boolean} active panel active
17255  * @cfg {String} html panel content
17256  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17257  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17258  * @cfg {String} href click to link..
17259  * 
17260  * 
17261  * @constructor
17262  * Create a new TabPanel
17263  * @param {Object} config The config object
17264  */
17265
17266 Roo.bootstrap.TabPanel = function(config){
17267     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17268     this.addEvents({
17269         /**
17270              * @event changed
17271              * Fires when the active status changes
17272              * @param {Roo.bootstrap.TabPanel} this
17273              * @param {Boolean} state the new state
17274             
17275          */
17276         'changed': true,
17277         /**
17278              * @event beforedeactivate
17279              * Fires before a tab is de-activated - can be used to do validation on a form.
17280              * @param {Roo.bootstrap.TabPanel} this
17281              * @return {Boolean} false if there is an error
17282             
17283          */
17284         'beforedeactivate': true
17285      });
17286     
17287     this.tabId = this.tabId || Roo.id();
17288   
17289 };
17290
17291 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17292     
17293     active: false,
17294     html: false,
17295     tabId: false,
17296     navId : false,
17297     href : '',
17298     
17299     getAutoCreate : function(){
17300         var cfg = {
17301             tag: 'div',
17302             // item is needed for carousel - not sure if it has any effect otherwise
17303             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17304             html: this.html || ''
17305         };
17306         
17307         if(this.active){
17308             cfg.cls += ' active';
17309         }
17310         
17311         if(this.tabId){
17312             cfg.tabId = this.tabId;
17313         }
17314         
17315         
17316         return cfg;
17317     },
17318     
17319     initEvents:  function()
17320     {
17321         var p = this.parent();
17322         this.navId = this.navId || p.navId;
17323         
17324         if (typeof(this.navId) != 'undefined') {
17325             // not really needed.. but just in case.. parent should be a NavGroup.
17326             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17327             
17328             tg.register(this);
17329             
17330             var i = tg.tabs.length - 1;
17331             
17332             if(this.active && tg.bullets > 0 && i < tg.bullets){
17333                 tg.setActiveBullet(i);
17334             }
17335         }
17336         
17337         if(this.href.length){
17338             this.el.on('click', this.onClick, this);
17339         }
17340         
17341     },
17342     
17343     onRender : function(ct, position)
17344     {
17345        // Roo.log("Call onRender: " + this.xtype);
17346         
17347         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17348         
17349         
17350         
17351         
17352         
17353     },
17354     
17355     setActive: function(state)
17356     {
17357         Roo.log("panel - set active " + this.tabId + "=" + state);
17358         
17359         this.active = state;
17360         if (!state) {
17361             this.el.removeClass('active');
17362             
17363         } else  if (!this.el.hasClass('active')) {
17364             this.el.addClass('active');
17365         }
17366         
17367         this.fireEvent('changed', this, state);
17368     },
17369     
17370     onClick: function(e)
17371     {
17372         e.preventDefault();
17373         
17374         window.location.href = this.href;
17375     }
17376     
17377     
17378 });
17379  
17380
17381  
17382
17383  /*
17384  * - LGPL
17385  *
17386  * DateField
17387  * 
17388  */
17389
17390 /**
17391  * @class Roo.bootstrap.DateField
17392  * @extends Roo.bootstrap.Input
17393  * Bootstrap DateField class
17394  * @cfg {Number} weekStart default 0
17395  * @cfg {String} viewMode default empty, (months|years)
17396  * @cfg {String} minViewMode default empty, (months|years)
17397  * @cfg {Number} startDate default -Infinity
17398  * @cfg {Number} endDate default Infinity
17399  * @cfg {Boolean} todayHighlight default false
17400  * @cfg {Boolean} todayBtn default false
17401  * @cfg {Boolean} calendarWeeks default false
17402  * @cfg {Object} daysOfWeekDisabled default empty
17403  * @cfg {Boolean} singleMode default false (true | false)
17404  * 
17405  * @cfg {Boolean} keyboardNavigation default true
17406  * @cfg {String} language default en
17407  * 
17408  * @constructor
17409  * Create a new DateField
17410  * @param {Object} config The config object
17411  */
17412
17413 Roo.bootstrap.DateField = function(config){
17414     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17415      this.addEvents({
17416             /**
17417              * @event show
17418              * Fires when this field show.
17419              * @param {Roo.bootstrap.DateField} this
17420              * @param {Mixed} date The date value
17421              */
17422             show : true,
17423             /**
17424              * @event show
17425              * Fires when this field hide.
17426              * @param {Roo.bootstrap.DateField} this
17427              * @param {Mixed} date The date value
17428              */
17429             hide : true,
17430             /**
17431              * @event select
17432              * Fires when select a date.
17433              * @param {Roo.bootstrap.DateField} this
17434              * @param {Mixed} date The date value
17435              */
17436             select : true,
17437             /**
17438              * @event beforeselect
17439              * Fires when before select a date.
17440              * @param {Roo.bootstrap.DateField} this
17441              * @param {Mixed} date The date value
17442              */
17443             beforeselect : true
17444         });
17445 };
17446
17447 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17448     
17449     /**
17450      * @cfg {String} format
17451      * The default date format string which can be overriden for localization support.  The format must be
17452      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17453      */
17454     format : "m/d/y",
17455     /**
17456      * @cfg {String} altFormats
17457      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17458      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17459      */
17460     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17461     
17462     weekStart : 0,
17463     
17464     viewMode : '',
17465     
17466     minViewMode : '',
17467     
17468     todayHighlight : false,
17469     
17470     todayBtn: false,
17471     
17472     language: 'en',
17473     
17474     keyboardNavigation: true,
17475     
17476     calendarWeeks: false,
17477     
17478     startDate: -Infinity,
17479     
17480     endDate: Infinity,
17481     
17482     daysOfWeekDisabled: [],
17483     
17484     _events: [],
17485     
17486     singleMode : false,
17487     
17488     UTCDate: function()
17489     {
17490         return new Date(Date.UTC.apply(Date, arguments));
17491     },
17492     
17493     UTCToday: function()
17494     {
17495         var today = new Date();
17496         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17497     },
17498     
17499     getDate: function() {
17500             var d = this.getUTCDate();
17501             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17502     },
17503     
17504     getUTCDate: function() {
17505             return this.date;
17506     },
17507     
17508     setDate: function(d) {
17509             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17510     },
17511     
17512     setUTCDate: function(d) {
17513             this.date = d;
17514             this.setValue(this.formatDate(this.date));
17515     },
17516         
17517     onRender: function(ct, position)
17518     {
17519         
17520         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17521         
17522         this.language = this.language || 'en';
17523         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17524         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17525         
17526         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17527         this.format = this.format || 'm/d/y';
17528         this.isInline = false;
17529         this.isInput = true;
17530         this.component = this.el.select('.add-on', true).first() || false;
17531         this.component = (this.component && this.component.length === 0) ? false : this.component;
17532         this.hasInput = this.component && this.inputEl().length;
17533         
17534         if (typeof(this.minViewMode === 'string')) {
17535             switch (this.minViewMode) {
17536                 case 'months':
17537                     this.minViewMode = 1;
17538                     break;
17539                 case 'years':
17540                     this.minViewMode = 2;
17541                     break;
17542                 default:
17543                     this.minViewMode = 0;
17544                     break;
17545             }
17546         }
17547         
17548         if (typeof(this.viewMode === 'string')) {
17549             switch (this.viewMode) {
17550                 case 'months':
17551                     this.viewMode = 1;
17552                     break;
17553                 case 'years':
17554                     this.viewMode = 2;
17555                     break;
17556                 default:
17557                     this.viewMode = 0;
17558                     break;
17559             }
17560         }
17561                 
17562         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17563         
17564 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17565         
17566         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17567         
17568         this.picker().on('mousedown', this.onMousedown, this);
17569         this.picker().on('click', this.onClick, this);
17570         
17571         this.picker().addClass('datepicker-dropdown');
17572         
17573         this.startViewMode = this.viewMode;
17574         
17575         if(this.singleMode){
17576             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17577                 v.setVisibilityMode(Roo.Element.DISPLAY);
17578                 v.hide();
17579             });
17580             
17581             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17582                 v.setStyle('width', '189px');
17583             });
17584         }
17585         
17586         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17587             if(!this.calendarWeeks){
17588                 v.remove();
17589                 return;
17590             }
17591             
17592             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17593             v.attr('colspan', function(i, val){
17594                 return parseInt(val) + 1;
17595             });
17596         });
17597                         
17598         
17599         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17600         
17601         this.setStartDate(this.startDate);
17602         this.setEndDate(this.endDate);
17603         
17604         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17605         
17606         this.fillDow();
17607         this.fillMonths();
17608         this.update();
17609         this.showMode();
17610         
17611         if(this.isInline) {
17612             this.show();
17613         }
17614     },
17615     
17616     picker : function()
17617     {
17618         return this.pickerEl;
17619 //        return this.el.select('.datepicker', true).first();
17620     },
17621     
17622     fillDow: function()
17623     {
17624         var dowCnt = this.weekStart;
17625         
17626         var dow = {
17627             tag: 'tr',
17628             cn: [
17629                 
17630             ]
17631         };
17632         
17633         if(this.calendarWeeks){
17634             dow.cn.push({
17635                 tag: 'th',
17636                 cls: 'cw',
17637                 html: '&nbsp;'
17638             })
17639         }
17640         
17641         while (dowCnt < this.weekStart + 7) {
17642             dow.cn.push({
17643                 tag: 'th',
17644                 cls: 'dow',
17645                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17646             });
17647         }
17648         
17649         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17650     },
17651     
17652     fillMonths: function()
17653     {    
17654         var i = 0;
17655         var months = this.picker().select('>.datepicker-months td', true).first();
17656         
17657         months.dom.innerHTML = '';
17658         
17659         while (i < 12) {
17660             var month = {
17661                 tag: 'span',
17662                 cls: 'month',
17663                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17664             };
17665             
17666             months.createChild(month);
17667         }
17668         
17669     },
17670     
17671     update: function()
17672     {
17673         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;
17674         
17675         if (this.date < this.startDate) {
17676             this.viewDate = new Date(this.startDate);
17677         } else if (this.date > this.endDate) {
17678             this.viewDate = new Date(this.endDate);
17679         } else {
17680             this.viewDate = new Date(this.date);
17681         }
17682         
17683         this.fill();
17684     },
17685     
17686     fill: function() 
17687     {
17688         var d = new Date(this.viewDate),
17689                 year = d.getUTCFullYear(),
17690                 month = d.getUTCMonth(),
17691                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17692                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17693                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17694                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17695                 currentDate = this.date && this.date.valueOf(),
17696                 today = this.UTCToday();
17697         
17698         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17699         
17700 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17701         
17702 //        this.picker.select('>tfoot th.today').
17703 //                                              .text(dates[this.language].today)
17704 //                                              .toggle(this.todayBtn !== false);
17705     
17706         this.updateNavArrows();
17707         this.fillMonths();
17708                                                 
17709         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17710         
17711         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17712          
17713         prevMonth.setUTCDate(day);
17714         
17715         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17716         
17717         var nextMonth = new Date(prevMonth);
17718         
17719         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17720         
17721         nextMonth = nextMonth.valueOf();
17722         
17723         var fillMonths = false;
17724         
17725         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17726         
17727         while(prevMonth.valueOf() < nextMonth) {
17728             var clsName = '';
17729             
17730             if (prevMonth.getUTCDay() === this.weekStart) {
17731                 if(fillMonths){
17732                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17733                 }
17734                     
17735                 fillMonths = {
17736                     tag: 'tr',
17737                     cn: []
17738                 };
17739                 
17740                 if(this.calendarWeeks){
17741                     // ISO 8601: First week contains first thursday.
17742                     // ISO also states week starts on Monday, but we can be more abstract here.
17743                     var
17744                     // Start of current week: based on weekstart/current date
17745                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17746                     // Thursday of this week
17747                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17748                     // First Thursday of year, year from thursday
17749                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17750                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17751                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17752                     
17753                     fillMonths.cn.push({
17754                         tag: 'td',
17755                         cls: 'cw',
17756                         html: calWeek
17757                     });
17758                 }
17759             }
17760             
17761             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17762                 clsName += ' old';
17763             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17764                 clsName += ' new';
17765             }
17766             if (this.todayHighlight &&
17767                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17768                 prevMonth.getUTCMonth() == today.getMonth() &&
17769                 prevMonth.getUTCDate() == today.getDate()) {
17770                 clsName += ' today';
17771             }
17772             
17773             if (currentDate && prevMonth.valueOf() === currentDate) {
17774                 clsName += ' active';
17775             }
17776             
17777             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17778                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17779                     clsName += ' disabled';
17780             }
17781             
17782             fillMonths.cn.push({
17783                 tag: 'td',
17784                 cls: 'day ' + clsName,
17785                 html: prevMonth.getDate()
17786             });
17787             
17788             prevMonth.setDate(prevMonth.getDate()+1);
17789         }
17790           
17791         var currentYear = this.date && this.date.getUTCFullYear();
17792         var currentMonth = this.date && this.date.getUTCMonth();
17793         
17794         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17795         
17796         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17797             v.removeClass('active');
17798             
17799             if(currentYear === year && k === currentMonth){
17800                 v.addClass('active');
17801             }
17802             
17803             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17804                 v.addClass('disabled');
17805             }
17806             
17807         });
17808         
17809         
17810         year = parseInt(year/10, 10) * 10;
17811         
17812         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17813         
17814         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17815         
17816         year -= 1;
17817         for (var i = -1; i < 11; i++) {
17818             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17819                 tag: 'span',
17820                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17821                 html: year
17822             });
17823             
17824             year += 1;
17825         }
17826     },
17827     
17828     showMode: function(dir) 
17829     {
17830         if (dir) {
17831             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17832         }
17833         
17834         Roo.each(this.picker().select('>div',true).elements, function(v){
17835             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17836             v.hide();
17837         });
17838         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17839     },
17840     
17841     place: function()
17842     {
17843         if(this.isInline) {
17844             return;
17845         }
17846         
17847         this.picker().removeClass(['bottom', 'top']);
17848         
17849         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17850             /*
17851              * place to the top of element!
17852              *
17853              */
17854             
17855             this.picker().addClass('top');
17856             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17857             
17858             return;
17859         }
17860         
17861         this.picker().addClass('bottom');
17862         
17863         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17864     },
17865     
17866     parseDate : function(value)
17867     {
17868         if(!value || value instanceof Date){
17869             return value;
17870         }
17871         var v = Date.parseDate(value, this.format);
17872         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17873             v = Date.parseDate(value, 'Y-m-d');
17874         }
17875         if(!v && this.altFormats){
17876             if(!this.altFormatsArray){
17877                 this.altFormatsArray = this.altFormats.split("|");
17878             }
17879             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17880                 v = Date.parseDate(value, this.altFormatsArray[i]);
17881             }
17882         }
17883         return v;
17884     },
17885     
17886     formatDate : function(date, fmt)
17887     {   
17888         return (!date || !(date instanceof Date)) ?
17889         date : date.dateFormat(fmt || this.format);
17890     },
17891     
17892     onFocus : function()
17893     {
17894         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17895         this.show();
17896     },
17897     
17898     onBlur : function()
17899     {
17900         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17901         
17902         var d = this.inputEl().getValue();
17903         
17904         this.setValue(d);
17905                 
17906         this.hide();
17907     },
17908     
17909     show : function()
17910     {
17911         this.picker().show();
17912         this.update();
17913         this.place();
17914         
17915         this.fireEvent('show', this, this.date);
17916     },
17917     
17918     hide : function()
17919     {
17920         if(this.isInline) {
17921             return;
17922         }
17923         this.picker().hide();
17924         this.viewMode = this.startViewMode;
17925         this.showMode();
17926         
17927         this.fireEvent('hide', this, this.date);
17928         
17929     },
17930     
17931     onMousedown: function(e)
17932     {
17933         e.stopPropagation();
17934         e.preventDefault();
17935     },
17936     
17937     keyup: function(e)
17938     {
17939         Roo.bootstrap.DateField.superclass.keyup.call(this);
17940         this.update();
17941     },
17942
17943     setValue: function(v)
17944     {
17945         if(this.fireEvent('beforeselect', this, v) !== false){
17946             var d = new Date(this.parseDate(v) ).clearTime();
17947         
17948             if(isNaN(d.getTime())){
17949                 this.date = this.viewDate = '';
17950                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17951                 return;
17952             }
17953
17954             v = this.formatDate(d);
17955
17956             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17957
17958             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17959
17960             this.update();
17961
17962             this.fireEvent('select', this, this.date);
17963         }
17964     },
17965     
17966     getValue: function()
17967     {
17968         return this.formatDate(this.date);
17969     },
17970     
17971     fireKey: function(e)
17972     {
17973         if (!this.picker().isVisible()){
17974             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17975                 this.show();
17976             }
17977             return;
17978         }
17979         
17980         var dateChanged = false,
17981         dir, day, month,
17982         newDate, newViewDate;
17983         
17984         switch(e.keyCode){
17985             case 27: // escape
17986                 this.hide();
17987                 e.preventDefault();
17988                 break;
17989             case 37: // left
17990             case 39: // right
17991                 if (!this.keyboardNavigation) {
17992                     break;
17993                 }
17994                 dir = e.keyCode == 37 ? -1 : 1;
17995                 
17996                 if (e.ctrlKey){
17997                     newDate = this.moveYear(this.date, dir);
17998                     newViewDate = this.moveYear(this.viewDate, dir);
17999                 } else if (e.shiftKey){
18000                     newDate = this.moveMonth(this.date, dir);
18001                     newViewDate = this.moveMonth(this.viewDate, dir);
18002                 } else {
18003                     newDate = new Date(this.date);
18004                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18005                     newViewDate = new Date(this.viewDate);
18006                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18007                 }
18008                 if (this.dateWithinRange(newDate)){
18009                     this.date = newDate;
18010                     this.viewDate = newViewDate;
18011                     this.setValue(this.formatDate(this.date));
18012 //                    this.update();
18013                     e.preventDefault();
18014                     dateChanged = true;
18015                 }
18016                 break;
18017             case 38: // up
18018             case 40: // down
18019                 if (!this.keyboardNavigation) {
18020                     break;
18021                 }
18022                 dir = e.keyCode == 38 ? -1 : 1;
18023                 if (e.ctrlKey){
18024                     newDate = this.moveYear(this.date, dir);
18025                     newViewDate = this.moveYear(this.viewDate, dir);
18026                 } else if (e.shiftKey){
18027                     newDate = this.moveMonth(this.date, dir);
18028                     newViewDate = this.moveMonth(this.viewDate, dir);
18029                 } else {
18030                     newDate = new Date(this.date);
18031                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18032                     newViewDate = new Date(this.viewDate);
18033                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18034                 }
18035                 if (this.dateWithinRange(newDate)){
18036                     this.date = newDate;
18037                     this.viewDate = newViewDate;
18038                     this.setValue(this.formatDate(this.date));
18039 //                    this.update();
18040                     e.preventDefault();
18041                     dateChanged = true;
18042                 }
18043                 break;
18044             case 13: // enter
18045                 this.setValue(this.formatDate(this.date));
18046                 this.hide();
18047                 e.preventDefault();
18048                 break;
18049             case 9: // tab
18050                 this.setValue(this.formatDate(this.date));
18051                 this.hide();
18052                 break;
18053             case 16: // shift
18054             case 17: // ctrl
18055             case 18: // alt
18056                 break;
18057             default :
18058                 this.hide();
18059                 
18060         }
18061     },
18062     
18063     
18064     onClick: function(e) 
18065     {
18066         e.stopPropagation();
18067         e.preventDefault();
18068         
18069         var target = e.getTarget();
18070         
18071         if(target.nodeName.toLowerCase() === 'i'){
18072             target = Roo.get(target).dom.parentNode;
18073         }
18074         
18075         var nodeName = target.nodeName;
18076         var className = target.className;
18077         var html = target.innerHTML;
18078         //Roo.log(nodeName);
18079         
18080         switch(nodeName.toLowerCase()) {
18081             case 'th':
18082                 switch(className) {
18083                     case 'switch':
18084                         this.showMode(1);
18085                         break;
18086                     case 'prev':
18087                     case 'next':
18088                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18089                         switch(this.viewMode){
18090                                 case 0:
18091                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18092                                         break;
18093                                 case 1:
18094                                 case 2:
18095                                         this.viewDate = this.moveYear(this.viewDate, dir);
18096                                         break;
18097                         }
18098                         this.fill();
18099                         break;
18100                     case 'today':
18101                         var date = new Date();
18102                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18103 //                        this.fill()
18104                         this.setValue(this.formatDate(this.date));
18105                         
18106                         this.hide();
18107                         break;
18108                 }
18109                 break;
18110             case 'span':
18111                 if (className.indexOf('disabled') < 0) {
18112                     this.viewDate.setUTCDate(1);
18113                     if (className.indexOf('month') > -1) {
18114                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18115                     } else {
18116                         var year = parseInt(html, 10) || 0;
18117                         this.viewDate.setUTCFullYear(year);
18118                         
18119                     }
18120                     
18121                     if(this.singleMode){
18122                         this.setValue(this.formatDate(this.viewDate));
18123                         this.hide();
18124                         return;
18125                     }
18126                     
18127                     this.showMode(-1);
18128                     this.fill();
18129                 }
18130                 break;
18131                 
18132             case 'td':
18133                 //Roo.log(className);
18134                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18135                     var day = parseInt(html, 10) || 1;
18136                     var year = this.viewDate.getUTCFullYear(),
18137                         month = this.viewDate.getUTCMonth();
18138
18139                     if (className.indexOf('old') > -1) {
18140                         if(month === 0 ){
18141                             month = 11;
18142                             year -= 1;
18143                         }else{
18144                             month -= 1;
18145                         }
18146                     } else if (className.indexOf('new') > -1) {
18147                         if (month == 11) {
18148                             month = 0;
18149                             year += 1;
18150                         } else {
18151                             month += 1;
18152                         }
18153                     }
18154                     //Roo.log([year,month,day]);
18155                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18156                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18157 //                    this.fill();
18158                     //Roo.log(this.formatDate(this.date));
18159                     this.setValue(this.formatDate(this.date));
18160                     this.hide();
18161                 }
18162                 break;
18163         }
18164     },
18165     
18166     setStartDate: function(startDate)
18167     {
18168         this.startDate = startDate || -Infinity;
18169         if (this.startDate !== -Infinity) {
18170             this.startDate = this.parseDate(this.startDate);
18171         }
18172         this.update();
18173         this.updateNavArrows();
18174     },
18175
18176     setEndDate: function(endDate)
18177     {
18178         this.endDate = endDate || Infinity;
18179         if (this.endDate !== Infinity) {
18180             this.endDate = this.parseDate(this.endDate);
18181         }
18182         this.update();
18183         this.updateNavArrows();
18184     },
18185     
18186     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18187     {
18188         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18189         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18190             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18191         }
18192         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18193             return parseInt(d, 10);
18194         });
18195         this.update();
18196         this.updateNavArrows();
18197     },
18198     
18199     updateNavArrows: function() 
18200     {
18201         if(this.singleMode){
18202             return;
18203         }
18204         
18205         var d = new Date(this.viewDate),
18206         year = d.getUTCFullYear(),
18207         month = d.getUTCMonth();
18208         
18209         Roo.each(this.picker().select('.prev', true).elements, function(v){
18210             v.show();
18211             switch (this.viewMode) {
18212                 case 0:
18213
18214                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18215                         v.hide();
18216                     }
18217                     break;
18218                 case 1:
18219                 case 2:
18220                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18221                         v.hide();
18222                     }
18223                     break;
18224             }
18225         });
18226         
18227         Roo.each(this.picker().select('.next', true).elements, function(v){
18228             v.show();
18229             switch (this.viewMode) {
18230                 case 0:
18231
18232                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18233                         v.hide();
18234                     }
18235                     break;
18236                 case 1:
18237                 case 2:
18238                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18239                         v.hide();
18240                     }
18241                     break;
18242             }
18243         })
18244     },
18245     
18246     moveMonth: function(date, dir)
18247     {
18248         if (!dir) {
18249             return date;
18250         }
18251         var new_date = new Date(date.valueOf()),
18252         day = new_date.getUTCDate(),
18253         month = new_date.getUTCMonth(),
18254         mag = Math.abs(dir),
18255         new_month, test;
18256         dir = dir > 0 ? 1 : -1;
18257         if (mag == 1){
18258             test = dir == -1
18259             // If going back one month, make sure month is not current month
18260             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18261             ? function(){
18262                 return new_date.getUTCMonth() == month;
18263             }
18264             // If going forward one month, make sure month is as expected
18265             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18266             : function(){
18267                 return new_date.getUTCMonth() != new_month;
18268             };
18269             new_month = month + dir;
18270             new_date.setUTCMonth(new_month);
18271             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18272             if (new_month < 0 || new_month > 11) {
18273                 new_month = (new_month + 12) % 12;
18274             }
18275         } else {
18276             // For magnitudes >1, move one month at a time...
18277             for (var i=0; i<mag; i++) {
18278                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18279                 new_date = this.moveMonth(new_date, dir);
18280             }
18281             // ...then reset the day, keeping it in the new month
18282             new_month = new_date.getUTCMonth();
18283             new_date.setUTCDate(day);
18284             test = function(){
18285                 return new_month != new_date.getUTCMonth();
18286             };
18287         }
18288         // Common date-resetting loop -- if date is beyond end of month, make it
18289         // end of month
18290         while (test()){
18291             new_date.setUTCDate(--day);
18292             new_date.setUTCMonth(new_month);
18293         }
18294         return new_date;
18295     },
18296
18297     moveYear: function(date, dir)
18298     {
18299         return this.moveMonth(date, dir*12);
18300     },
18301
18302     dateWithinRange: function(date)
18303     {
18304         return date >= this.startDate && date <= this.endDate;
18305     },
18306
18307     
18308     remove: function() 
18309     {
18310         this.picker().remove();
18311     },
18312     
18313     validateValue : function(value)
18314     {
18315         if(value.length < 1)  {
18316             if(this.allowBlank){
18317                 return true;
18318             }
18319             return false;
18320         }
18321         
18322         if(value.length < this.minLength){
18323             return false;
18324         }
18325         if(value.length > this.maxLength){
18326             return false;
18327         }
18328         if(this.vtype){
18329             var vt = Roo.form.VTypes;
18330             if(!vt[this.vtype](value, this)){
18331                 return false;
18332             }
18333         }
18334         if(typeof this.validator == "function"){
18335             var msg = this.validator(value);
18336             if(msg !== true){
18337                 return false;
18338             }
18339         }
18340         
18341         if(this.regex && !this.regex.test(value)){
18342             return false;
18343         }
18344         
18345         if(typeof(this.parseDate(value)) == 'undefined'){
18346             return false;
18347         }
18348         
18349         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18350             return false;
18351         }      
18352         
18353         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18354             return false;
18355         } 
18356         
18357         
18358         return true;
18359     }
18360    
18361 });
18362
18363 Roo.apply(Roo.bootstrap.DateField,  {
18364     
18365     head : {
18366         tag: 'thead',
18367         cn: [
18368         {
18369             tag: 'tr',
18370             cn: [
18371             {
18372                 tag: 'th',
18373                 cls: 'prev',
18374                 html: '<i class="fa fa-arrow-left"/>'
18375             },
18376             {
18377                 tag: 'th',
18378                 cls: 'switch',
18379                 colspan: '5'
18380             },
18381             {
18382                 tag: 'th',
18383                 cls: 'next',
18384                 html: '<i class="fa fa-arrow-right"/>'
18385             }
18386
18387             ]
18388         }
18389         ]
18390     },
18391     
18392     content : {
18393         tag: 'tbody',
18394         cn: [
18395         {
18396             tag: 'tr',
18397             cn: [
18398             {
18399                 tag: 'td',
18400                 colspan: '7'
18401             }
18402             ]
18403         }
18404         ]
18405     },
18406     
18407     footer : {
18408         tag: 'tfoot',
18409         cn: [
18410         {
18411             tag: 'tr',
18412             cn: [
18413             {
18414                 tag: 'th',
18415                 colspan: '7',
18416                 cls: 'today'
18417             }
18418                     
18419             ]
18420         }
18421         ]
18422     },
18423     
18424     dates:{
18425         en: {
18426             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18427             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18428             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18429             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18430             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18431             today: "Today"
18432         }
18433     },
18434     
18435     modes: [
18436     {
18437         clsName: 'days',
18438         navFnc: 'Month',
18439         navStep: 1
18440     },
18441     {
18442         clsName: 'months',
18443         navFnc: 'FullYear',
18444         navStep: 1
18445     },
18446     {
18447         clsName: 'years',
18448         navFnc: 'FullYear',
18449         navStep: 10
18450     }]
18451 });
18452
18453 Roo.apply(Roo.bootstrap.DateField,  {
18454   
18455     template : {
18456         tag: 'div',
18457         cls: 'datepicker dropdown-menu roo-dynamic',
18458         cn: [
18459         {
18460             tag: 'div',
18461             cls: 'datepicker-days',
18462             cn: [
18463             {
18464                 tag: 'table',
18465                 cls: 'table-condensed',
18466                 cn:[
18467                 Roo.bootstrap.DateField.head,
18468                 {
18469                     tag: 'tbody'
18470                 },
18471                 Roo.bootstrap.DateField.footer
18472                 ]
18473             }
18474             ]
18475         },
18476         {
18477             tag: 'div',
18478             cls: 'datepicker-months',
18479             cn: [
18480             {
18481                 tag: 'table',
18482                 cls: 'table-condensed',
18483                 cn:[
18484                 Roo.bootstrap.DateField.head,
18485                 Roo.bootstrap.DateField.content,
18486                 Roo.bootstrap.DateField.footer
18487                 ]
18488             }
18489             ]
18490         },
18491         {
18492             tag: 'div',
18493             cls: 'datepicker-years',
18494             cn: [
18495             {
18496                 tag: 'table',
18497                 cls: 'table-condensed',
18498                 cn:[
18499                 Roo.bootstrap.DateField.head,
18500                 Roo.bootstrap.DateField.content,
18501                 Roo.bootstrap.DateField.footer
18502                 ]
18503             }
18504             ]
18505         }
18506         ]
18507     }
18508 });
18509
18510  
18511
18512  /*
18513  * - LGPL
18514  *
18515  * TimeField
18516  * 
18517  */
18518
18519 /**
18520  * @class Roo.bootstrap.TimeField
18521  * @extends Roo.bootstrap.Input
18522  * Bootstrap DateField class
18523  * 
18524  * 
18525  * @constructor
18526  * Create a new TimeField
18527  * @param {Object} config The config object
18528  */
18529
18530 Roo.bootstrap.TimeField = function(config){
18531     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18532     this.addEvents({
18533             /**
18534              * @event show
18535              * Fires when this field show.
18536              * @param {Roo.bootstrap.DateField} thisthis
18537              * @param {Mixed} date The date value
18538              */
18539             show : true,
18540             /**
18541              * @event show
18542              * Fires when this field hide.
18543              * @param {Roo.bootstrap.DateField} this
18544              * @param {Mixed} date The date value
18545              */
18546             hide : true,
18547             /**
18548              * @event select
18549              * Fires when select a date.
18550              * @param {Roo.bootstrap.DateField} this
18551              * @param {Mixed} date The date value
18552              */
18553             select : true
18554         });
18555 };
18556
18557 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18558     
18559     /**
18560      * @cfg {String} format
18561      * The default time format string which can be overriden for localization support.  The format must be
18562      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18563      */
18564     format : "H:i",
18565        
18566     onRender: function(ct, position)
18567     {
18568         
18569         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18570                 
18571         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18572         
18573         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18574         
18575         this.pop = this.picker().select('>.datepicker-time',true).first();
18576         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18577         
18578         this.picker().on('mousedown', this.onMousedown, this);
18579         this.picker().on('click', this.onClick, this);
18580         
18581         this.picker().addClass('datepicker-dropdown');
18582     
18583         this.fillTime();
18584         this.update();
18585             
18586         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18587         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18588         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18589         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18590         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18591         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18592
18593     },
18594     
18595     fireKey: function(e){
18596         if (!this.picker().isVisible()){
18597             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18598                 this.show();
18599             }
18600             return;
18601         }
18602
18603         e.preventDefault();
18604         
18605         switch(e.keyCode){
18606             case 27: // escape
18607                 this.hide();
18608                 break;
18609             case 37: // left
18610             case 39: // right
18611                 this.onTogglePeriod();
18612                 break;
18613             case 38: // up
18614                 this.onIncrementMinutes();
18615                 break;
18616             case 40: // down
18617                 this.onDecrementMinutes();
18618                 break;
18619             case 13: // enter
18620             case 9: // tab
18621                 this.setTime();
18622                 break;
18623         }
18624     },
18625     
18626     onClick: function(e) {
18627         e.stopPropagation();
18628         e.preventDefault();
18629     },
18630     
18631     picker : function()
18632     {
18633         return this.el.select('.datepicker', true).first();
18634     },
18635     
18636     fillTime: function()
18637     {    
18638         var time = this.pop.select('tbody', true).first();
18639         
18640         time.dom.innerHTML = '';
18641         
18642         time.createChild({
18643             tag: 'tr',
18644             cn: [
18645                 {
18646                     tag: 'td',
18647                     cn: [
18648                         {
18649                             tag: 'a',
18650                             href: '#',
18651                             cls: 'btn',
18652                             cn: [
18653                                 {
18654                                     tag: 'span',
18655                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18656                                 }
18657                             ]
18658                         } 
18659                     ]
18660                 },
18661                 {
18662                     tag: 'td',
18663                     cls: 'separator'
18664                 },
18665                 {
18666                     tag: 'td',
18667                     cn: [
18668                         {
18669                             tag: 'a',
18670                             href: '#',
18671                             cls: 'btn',
18672                             cn: [
18673                                 {
18674                                     tag: 'span',
18675                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18676                                 }
18677                             ]
18678                         }
18679                     ]
18680                 },
18681                 {
18682                     tag: 'td',
18683                     cls: 'separator'
18684                 }
18685             ]
18686         });
18687         
18688         time.createChild({
18689             tag: 'tr',
18690             cn: [
18691                 {
18692                     tag: 'td',
18693                     cn: [
18694                         {
18695                             tag: 'span',
18696                             cls: 'timepicker-hour',
18697                             html: '00'
18698                         }  
18699                     ]
18700                 },
18701                 {
18702                     tag: 'td',
18703                     cls: 'separator',
18704                     html: ':'
18705                 },
18706                 {
18707                     tag: 'td',
18708                     cn: [
18709                         {
18710                             tag: 'span',
18711                             cls: 'timepicker-minute',
18712                             html: '00'
18713                         }  
18714                     ]
18715                 },
18716                 {
18717                     tag: 'td',
18718                     cls: 'separator'
18719                 },
18720                 {
18721                     tag: 'td',
18722                     cn: [
18723                         {
18724                             tag: 'button',
18725                             type: 'button',
18726                             cls: 'btn btn-primary period',
18727                             html: 'AM'
18728                             
18729                         }
18730                     ]
18731                 }
18732             ]
18733         });
18734         
18735         time.createChild({
18736             tag: 'tr',
18737             cn: [
18738                 {
18739                     tag: 'td',
18740                     cn: [
18741                         {
18742                             tag: 'a',
18743                             href: '#',
18744                             cls: 'btn',
18745                             cn: [
18746                                 {
18747                                     tag: 'span',
18748                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18749                                 }
18750                             ]
18751                         }
18752                     ]
18753                 },
18754                 {
18755                     tag: 'td',
18756                     cls: 'separator'
18757                 },
18758                 {
18759                     tag: 'td',
18760                     cn: [
18761                         {
18762                             tag: 'a',
18763                             href: '#',
18764                             cls: 'btn',
18765                             cn: [
18766                                 {
18767                                     tag: 'span',
18768                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18769                                 }
18770                             ]
18771                         }
18772                     ]
18773                 },
18774                 {
18775                     tag: 'td',
18776                     cls: 'separator'
18777                 }
18778             ]
18779         });
18780         
18781     },
18782     
18783     update: function()
18784     {
18785         
18786         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18787         
18788         this.fill();
18789     },
18790     
18791     fill: function() 
18792     {
18793         var hours = this.time.getHours();
18794         var minutes = this.time.getMinutes();
18795         var period = 'AM';
18796         
18797         if(hours > 11){
18798             period = 'PM';
18799         }
18800         
18801         if(hours == 0){
18802             hours = 12;
18803         }
18804         
18805         
18806         if(hours > 12){
18807             hours = hours - 12;
18808         }
18809         
18810         if(hours < 10){
18811             hours = '0' + hours;
18812         }
18813         
18814         if(minutes < 10){
18815             minutes = '0' + minutes;
18816         }
18817         
18818         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18819         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18820         this.pop.select('button', true).first().dom.innerHTML = period;
18821         
18822     },
18823     
18824     place: function()
18825     {   
18826         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18827         
18828         var cls = ['bottom'];
18829         
18830         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18831             cls.pop();
18832             cls.push('top');
18833         }
18834         
18835         cls.push('right');
18836         
18837         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18838             cls.pop();
18839             cls.push('left');
18840         }
18841         
18842         this.picker().addClass(cls.join('-'));
18843         
18844         var _this = this;
18845         
18846         Roo.each(cls, function(c){
18847             if(c == 'bottom'){
18848                 _this.picker().setTop(_this.inputEl().getHeight());
18849                 return;
18850             }
18851             if(c == 'top'){
18852                 _this.picker().setTop(0 - _this.picker().getHeight());
18853                 return;
18854             }
18855             
18856             if(c == 'left'){
18857                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18858                 return;
18859             }
18860             if(c == 'right'){
18861                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18862                 return;
18863             }
18864         });
18865         
18866     },
18867   
18868     onFocus : function()
18869     {
18870         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18871         this.show();
18872     },
18873     
18874     onBlur : function()
18875     {
18876         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18877         this.hide();
18878     },
18879     
18880     show : function()
18881     {
18882         this.picker().show();
18883         this.pop.show();
18884         this.update();
18885         this.place();
18886         
18887         this.fireEvent('show', this, this.date);
18888     },
18889     
18890     hide : function()
18891     {
18892         this.picker().hide();
18893         this.pop.hide();
18894         
18895         this.fireEvent('hide', this, this.date);
18896     },
18897     
18898     setTime : function()
18899     {
18900         this.hide();
18901         this.setValue(this.time.format(this.format));
18902         
18903         this.fireEvent('select', this, this.date);
18904         
18905         
18906     },
18907     
18908     onMousedown: function(e){
18909         e.stopPropagation();
18910         e.preventDefault();
18911     },
18912     
18913     onIncrementHours: function()
18914     {
18915         Roo.log('onIncrementHours');
18916         this.time = this.time.add(Date.HOUR, 1);
18917         this.update();
18918         
18919     },
18920     
18921     onDecrementHours: function()
18922     {
18923         Roo.log('onDecrementHours');
18924         this.time = this.time.add(Date.HOUR, -1);
18925         this.update();
18926     },
18927     
18928     onIncrementMinutes: function()
18929     {
18930         Roo.log('onIncrementMinutes');
18931         this.time = this.time.add(Date.MINUTE, 1);
18932         this.update();
18933     },
18934     
18935     onDecrementMinutes: function()
18936     {
18937         Roo.log('onDecrementMinutes');
18938         this.time = this.time.add(Date.MINUTE, -1);
18939         this.update();
18940     },
18941     
18942     onTogglePeriod: function()
18943     {
18944         Roo.log('onTogglePeriod');
18945         this.time = this.time.add(Date.HOUR, 12);
18946         this.update();
18947     }
18948     
18949    
18950 });
18951
18952 Roo.apply(Roo.bootstrap.TimeField,  {
18953     
18954     content : {
18955         tag: 'tbody',
18956         cn: [
18957             {
18958                 tag: 'tr',
18959                 cn: [
18960                 {
18961                     tag: 'td',
18962                     colspan: '7'
18963                 }
18964                 ]
18965             }
18966         ]
18967     },
18968     
18969     footer : {
18970         tag: 'tfoot',
18971         cn: [
18972             {
18973                 tag: 'tr',
18974                 cn: [
18975                 {
18976                     tag: 'th',
18977                     colspan: '7',
18978                     cls: '',
18979                     cn: [
18980                         {
18981                             tag: 'button',
18982                             cls: 'btn btn-info ok',
18983                             html: 'OK'
18984                         }
18985                     ]
18986                 }
18987
18988                 ]
18989             }
18990         ]
18991     }
18992 });
18993
18994 Roo.apply(Roo.bootstrap.TimeField,  {
18995   
18996     template : {
18997         tag: 'div',
18998         cls: 'datepicker dropdown-menu',
18999         cn: [
19000             {
19001                 tag: 'div',
19002                 cls: 'datepicker-time',
19003                 cn: [
19004                 {
19005                     tag: 'table',
19006                     cls: 'table-condensed',
19007                     cn:[
19008                     Roo.bootstrap.TimeField.content,
19009                     Roo.bootstrap.TimeField.footer
19010                     ]
19011                 }
19012                 ]
19013             }
19014         ]
19015     }
19016 });
19017
19018  
19019
19020  /*
19021  * - LGPL
19022  *
19023  * MonthField
19024  * 
19025  */
19026
19027 /**
19028  * @class Roo.bootstrap.MonthField
19029  * @extends Roo.bootstrap.Input
19030  * Bootstrap MonthField class
19031  * 
19032  * @cfg {String} language default en
19033  * 
19034  * @constructor
19035  * Create a new MonthField
19036  * @param {Object} config The config object
19037  */
19038
19039 Roo.bootstrap.MonthField = function(config){
19040     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19041     
19042     this.addEvents({
19043         /**
19044          * @event show
19045          * Fires when this field show.
19046          * @param {Roo.bootstrap.MonthField} this
19047          * @param {Mixed} date The date value
19048          */
19049         show : true,
19050         /**
19051          * @event show
19052          * Fires when this field hide.
19053          * @param {Roo.bootstrap.MonthField} this
19054          * @param {Mixed} date The date value
19055          */
19056         hide : true,
19057         /**
19058          * @event select
19059          * Fires when select a date.
19060          * @param {Roo.bootstrap.MonthField} this
19061          * @param {String} oldvalue The old value
19062          * @param {String} newvalue The new value
19063          */
19064         select : true
19065     });
19066 };
19067
19068 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19069     
19070     onRender: function(ct, position)
19071     {
19072         
19073         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19074         
19075         this.language = this.language || 'en';
19076         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19077         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19078         
19079         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19080         this.isInline = false;
19081         this.isInput = true;
19082         this.component = this.el.select('.add-on', true).first() || false;
19083         this.component = (this.component && this.component.length === 0) ? false : this.component;
19084         this.hasInput = this.component && this.inputEL().length;
19085         
19086         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19087         
19088         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19089         
19090         this.picker().on('mousedown', this.onMousedown, this);
19091         this.picker().on('click', this.onClick, this);
19092         
19093         this.picker().addClass('datepicker-dropdown');
19094         
19095         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19096             v.setStyle('width', '189px');
19097         });
19098         
19099         this.fillMonths();
19100         
19101         this.update();
19102         
19103         if(this.isInline) {
19104             this.show();
19105         }
19106         
19107     },
19108     
19109     setValue: function(v, suppressEvent)
19110     {   
19111         var o = this.getValue();
19112         
19113         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19114         
19115         this.update();
19116
19117         if(suppressEvent !== true){
19118             this.fireEvent('select', this, o, v);
19119         }
19120         
19121     },
19122     
19123     getValue: function()
19124     {
19125         return this.value;
19126     },
19127     
19128     onClick: function(e) 
19129     {
19130         e.stopPropagation();
19131         e.preventDefault();
19132         
19133         var target = e.getTarget();
19134         
19135         if(target.nodeName.toLowerCase() === 'i'){
19136             target = Roo.get(target).dom.parentNode;
19137         }
19138         
19139         var nodeName = target.nodeName;
19140         var className = target.className;
19141         var html = target.innerHTML;
19142         
19143         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19144             return;
19145         }
19146         
19147         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19148         
19149         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19150         
19151         this.hide();
19152                         
19153     },
19154     
19155     picker : function()
19156     {
19157         return this.pickerEl;
19158     },
19159     
19160     fillMonths: function()
19161     {    
19162         var i = 0;
19163         var months = this.picker().select('>.datepicker-months td', true).first();
19164         
19165         months.dom.innerHTML = '';
19166         
19167         while (i < 12) {
19168             var month = {
19169                 tag: 'span',
19170                 cls: 'month',
19171                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19172             };
19173             
19174             months.createChild(month);
19175         }
19176         
19177     },
19178     
19179     update: function()
19180     {
19181         var _this = this;
19182         
19183         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19184             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19185         }
19186         
19187         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19188             e.removeClass('active');
19189             
19190             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19191                 e.addClass('active');
19192             }
19193         })
19194     },
19195     
19196     place: function()
19197     {
19198         if(this.isInline) {
19199             return;
19200         }
19201         
19202         this.picker().removeClass(['bottom', 'top']);
19203         
19204         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19205             /*
19206              * place to the top of element!
19207              *
19208              */
19209             
19210             this.picker().addClass('top');
19211             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19212             
19213             return;
19214         }
19215         
19216         this.picker().addClass('bottom');
19217         
19218         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19219     },
19220     
19221     onFocus : function()
19222     {
19223         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19224         this.show();
19225     },
19226     
19227     onBlur : function()
19228     {
19229         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19230         
19231         var d = this.inputEl().getValue();
19232         
19233         this.setValue(d);
19234                 
19235         this.hide();
19236     },
19237     
19238     show : function()
19239     {
19240         this.picker().show();
19241         this.picker().select('>.datepicker-months', true).first().show();
19242         this.update();
19243         this.place();
19244         
19245         this.fireEvent('show', this, this.date);
19246     },
19247     
19248     hide : function()
19249     {
19250         if(this.isInline) {
19251             return;
19252         }
19253         this.picker().hide();
19254         this.fireEvent('hide', this, this.date);
19255         
19256     },
19257     
19258     onMousedown: function(e)
19259     {
19260         e.stopPropagation();
19261         e.preventDefault();
19262     },
19263     
19264     keyup: function(e)
19265     {
19266         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19267         this.update();
19268     },
19269
19270     fireKey: function(e)
19271     {
19272         if (!this.picker().isVisible()){
19273             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19274                 this.show();
19275             }
19276             return;
19277         }
19278         
19279         var dir;
19280         
19281         switch(e.keyCode){
19282             case 27: // escape
19283                 this.hide();
19284                 e.preventDefault();
19285                 break;
19286             case 37: // left
19287             case 39: // right
19288                 dir = e.keyCode == 37 ? -1 : 1;
19289                 
19290                 this.vIndex = this.vIndex + dir;
19291                 
19292                 if(this.vIndex < 0){
19293                     this.vIndex = 0;
19294                 }
19295                 
19296                 if(this.vIndex > 11){
19297                     this.vIndex = 11;
19298                 }
19299                 
19300                 if(isNaN(this.vIndex)){
19301                     this.vIndex = 0;
19302                 }
19303                 
19304                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19305                 
19306                 break;
19307             case 38: // up
19308             case 40: // down
19309                 
19310                 dir = e.keyCode == 38 ? -1 : 1;
19311                 
19312                 this.vIndex = this.vIndex + dir * 4;
19313                 
19314                 if(this.vIndex < 0){
19315                     this.vIndex = 0;
19316                 }
19317                 
19318                 if(this.vIndex > 11){
19319                     this.vIndex = 11;
19320                 }
19321                 
19322                 if(isNaN(this.vIndex)){
19323                     this.vIndex = 0;
19324                 }
19325                 
19326                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19327                 break;
19328                 
19329             case 13: // enter
19330                 
19331                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19332                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19333                 }
19334                 
19335                 this.hide();
19336                 e.preventDefault();
19337                 break;
19338             case 9: // tab
19339                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19340                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19341                 }
19342                 this.hide();
19343                 break;
19344             case 16: // shift
19345             case 17: // ctrl
19346             case 18: // alt
19347                 break;
19348             default :
19349                 this.hide();
19350                 
19351         }
19352     },
19353     
19354     remove: function() 
19355     {
19356         this.picker().remove();
19357     }
19358    
19359 });
19360
19361 Roo.apply(Roo.bootstrap.MonthField,  {
19362     
19363     content : {
19364         tag: 'tbody',
19365         cn: [
19366         {
19367             tag: 'tr',
19368             cn: [
19369             {
19370                 tag: 'td',
19371                 colspan: '7'
19372             }
19373             ]
19374         }
19375         ]
19376     },
19377     
19378     dates:{
19379         en: {
19380             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19381             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19382         }
19383     }
19384 });
19385
19386 Roo.apply(Roo.bootstrap.MonthField,  {
19387   
19388     template : {
19389         tag: 'div',
19390         cls: 'datepicker dropdown-menu roo-dynamic',
19391         cn: [
19392             {
19393                 tag: 'div',
19394                 cls: 'datepicker-months',
19395                 cn: [
19396                 {
19397                     tag: 'table',
19398                     cls: 'table-condensed',
19399                     cn:[
19400                         Roo.bootstrap.DateField.content
19401                     ]
19402                 }
19403                 ]
19404             }
19405         ]
19406     }
19407 });
19408
19409  
19410
19411  
19412  /*
19413  * - LGPL
19414  *
19415  * CheckBox
19416  * 
19417  */
19418
19419 /**
19420  * @class Roo.bootstrap.CheckBox
19421  * @extends Roo.bootstrap.Input
19422  * Bootstrap CheckBox class
19423  * 
19424  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19425  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19426  * @cfg {String} boxLabel The text that appears beside the checkbox
19427  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19428  * @cfg {Boolean} checked initnal the element
19429  * @cfg {Boolean} inline inline the element (default false)
19430  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19431  * 
19432  * @constructor
19433  * Create a new CheckBox
19434  * @param {Object} config The config object
19435  */
19436
19437 Roo.bootstrap.CheckBox = function(config){
19438     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19439    
19440     this.addEvents({
19441         /**
19442         * @event check
19443         * Fires when the element is checked or unchecked.
19444         * @param {Roo.bootstrap.CheckBox} this This input
19445         * @param {Boolean} checked The new checked value
19446         */
19447        check : true
19448     });
19449     
19450 };
19451
19452 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19453   
19454     inputType: 'checkbox',
19455     inputValue: 1,
19456     valueOff: 0,
19457     boxLabel: false,
19458     checked: false,
19459     weight : false,
19460     inline: false,
19461     
19462     getAutoCreate : function()
19463     {
19464         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19465         
19466         var id = Roo.id();
19467         
19468         var cfg = {};
19469         
19470         cfg.cls = 'form-group ' + this.inputType; //input-group
19471         
19472         if(this.inline){
19473             cfg.cls += ' ' + this.inputType + '-inline';
19474         }
19475         
19476         var input =  {
19477             tag: 'input',
19478             id : id,
19479             type : this.inputType,
19480             value : this.inputValue,
19481             cls : 'roo-' + this.inputType, //'form-box',
19482             placeholder : this.placeholder || ''
19483             
19484         };
19485         
19486         if(this.inputType != 'radio'){
19487             var hidden =  {
19488                 tag: 'input',
19489                 type : 'hidden',
19490                 cls : 'roo-hidden-value',
19491                 value : this.checked ? this.valueOff : this.inputValue
19492             };
19493         }
19494         
19495             
19496         if (this.weight) { // Validity check?
19497             cfg.cls += " " + this.inputType + "-" + this.weight;
19498         }
19499         
19500         if (this.disabled) {
19501             input.disabled=true;
19502         }
19503         
19504         if(this.checked){
19505             input.checked = this.checked;
19506             
19507         }
19508         
19509         
19510         if (this.name) {
19511             
19512             input.name = this.name;
19513             
19514             if(this.inputType != 'radio'){
19515                 hidden.name = this.name;
19516                 input.name = '_hidden_' + this.name;
19517             }
19518         }
19519         
19520         if (this.size) {
19521             input.cls += ' input-' + this.size;
19522         }
19523         
19524         var settings=this;
19525         
19526         ['xs','sm','md','lg'].map(function(size){
19527             if (settings[size]) {
19528                 cfg.cls += ' col-' + size + '-' + settings[size];
19529             }
19530         });
19531         
19532         var inputblock = input;
19533          
19534         if (this.before || this.after) {
19535             
19536             inputblock = {
19537                 cls : 'input-group',
19538                 cn :  [] 
19539             };
19540             
19541             if (this.before) {
19542                 inputblock.cn.push({
19543                     tag :'span',
19544                     cls : 'input-group-addon',
19545                     html : this.before
19546                 });
19547             }
19548             
19549             inputblock.cn.push(input);
19550             
19551             if(this.inputType != 'radio'){
19552                 inputblock.cn.push(hidden);
19553             }
19554             
19555             if (this.after) {
19556                 inputblock.cn.push({
19557                     tag :'span',
19558                     cls : 'input-group-addon',
19559                     html : this.after
19560                 });
19561             }
19562             
19563         }
19564         
19565         if (align ==='left' && this.fieldLabel.length) {
19566 //                Roo.log("left and has label");
19567                 cfg.cn = [
19568                     
19569                     {
19570                         tag: 'label',
19571                         'for' :  id,
19572                         cls : 'control-label col-md-' + this.labelWidth,
19573                         html : this.fieldLabel
19574                         
19575                     },
19576                     {
19577                         cls : "col-md-" + (12 - this.labelWidth), 
19578                         cn: [
19579                             inputblock
19580                         ]
19581                     }
19582                     
19583                 ];
19584         } else if ( this.fieldLabel.length) {
19585 //                Roo.log(" label");
19586                 cfg.cn = [
19587                    
19588                     {
19589                         tag: this.boxLabel ? 'span' : 'label',
19590                         'for': id,
19591                         cls: 'control-label box-input-label',
19592                         //cls : 'input-group-addon',
19593                         html : this.fieldLabel
19594                         
19595                     },
19596                     
19597                     inputblock
19598                     
19599                 ];
19600
19601         } else {
19602             
19603 //                Roo.log(" no label && no align");
19604                 cfg.cn = [  inputblock ] ;
19605                 
19606                 
19607         }
19608         
19609         if(this.boxLabel){
19610              var boxLabelCfg = {
19611                 tag: 'label',
19612                 //'for': id, // box label is handled by onclick - so no for...
19613                 cls: 'box-label',
19614                 html: this.boxLabel
19615             };
19616             
19617             if(this.tooltip){
19618                 boxLabelCfg.tooltip = this.tooltip;
19619             }
19620              
19621             cfg.cn.push(boxLabelCfg);
19622         }
19623         
19624         if(this.inputType != 'radio'){
19625             cfg.cn.push(hidden);
19626         }
19627         
19628         return cfg;
19629         
19630     },
19631     
19632     /**
19633      * return the real input element.
19634      */
19635     inputEl: function ()
19636     {
19637         return this.el.select('input.roo-' + this.inputType,true).first();
19638     },
19639     hiddenEl: function ()
19640     {
19641         return this.el.select('input.roo-hidden-value',true).first();
19642     },
19643     
19644     labelEl: function()
19645     {
19646         return this.el.select('label.control-label',true).first();
19647     },
19648     /* depricated... */
19649     
19650     label: function()
19651     {
19652         return this.labelEl();
19653     },
19654     
19655     boxLabelEl: function()
19656     {
19657         return this.el.select('label.box-label',true).first();
19658     },
19659     
19660     initEvents : function()
19661     {
19662 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19663         
19664         this.inputEl().on('click', this.onClick,  this);
19665         
19666         if (this.boxLabel) { 
19667             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19668         }
19669         
19670         this.startValue = this.getValue();
19671         
19672         if(this.groupId){
19673             Roo.bootstrap.CheckBox.register(this);
19674         }
19675     },
19676     
19677     onClick : function()
19678     {   
19679         this.setChecked(!this.checked);
19680     },
19681     
19682     setChecked : function(state,suppressEvent)
19683     {
19684         this.startValue = this.getValue();
19685         
19686         if(this.inputType == 'radio'){
19687             
19688             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19689                 e.dom.checked = false;
19690             });
19691             
19692             this.inputEl().dom.checked = true;
19693             
19694             this.inputEl().dom.value = this.inputValue;
19695             
19696             if(suppressEvent !== true){
19697                 this.fireEvent('check', this, true);
19698             }
19699             
19700             this.validate();
19701             
19702             return;
19703         }
19704         
19705         this.checked = state;
19706         
19707         this.inputEl().dom.checked = state;
19708         
19709         
19710         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19711         
19712         if(suppressEvent !== true){
19713             this.fireEvent('check', this, state);
19714         }
19715         
19716         this.validate();
19717     },
19718     
19719     getValue : function()
19720     {
19721         if(this.inputType == 'radio'){
19722             return this.getGroupValue();
19723         }
19724         
19725         return this.hiddenEl().dom.value;
19726         
19727     },
19728     
19729     getGroupValue : function()
19730     {
19731         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19732             return '';
19733         }
19734         
19735         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19736     },
19737     
19738     setValue : function(v,suppressEvent)
19739     {
19740         if(this.inputType == 'radio'){
19741             this.setGroupValue(v, suppressEvent);
19742             return;
19743         }
19744         
19745         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19746         
19747         this.validate();
19748     },
19749     
19750     setGroupValue : function(v, suppressEvent)
19751     {
19752         this.startValue = this.getValue();
19753         
19754         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19755             e.dom.checked = false;
19756             
19757             if(e.dom.value == v){
19758                 e.dom.checked = true;
19759             }
19760         });
19761         
19762         if(suppressEvent !== true){
19763             this.fireEvent('check', this, true);
19764         }
19765
19766         this.validate();
19767         
19768         return;
19769     },
19770     
19771     validate : function()
19772     {
19773         if(
19774                 this.disabled || 
19775                 (this.inputType == 'radio' && this.validateRadio()) ||
19776                 (this.inputType == 'checkbox' && this.validateCheckbox())
19777         ){
19778             this.markValid();
19779             return true;
19780         }
19781         
19782         this.markInvalid();
19783         return false;
19784     },
19785     
19786     validateRadio : function()
19787     {
19788         if(this.allowBlank){
19789             return true;
19790         }
19791         
19792         var valid = false;
19793         
19794         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19795             if(!e.dom.checked){
19796                 return;
19797             }
19798             
19799             valid = true;
19800             
19801             return false;
19802         });
19803         
19804         return valid;
19805     },
19806     
19807     validateCheckbox : function()
19808     {
19809         if(!this.groupId){
19810             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19811         }
19812         
19813         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19814         
19815         if(!group){
19816             return false;
19817         }
19818         
19819         var r = false;
19820         
19821         for(var i in group){
19822             if(r){
19823                 break;
19824             }
19825             
19826             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19827         }
19828         
19829         return r;
19830     },
19831     
19832     /**
19833      * Mark this field as valid
19834      */
19835     markValid : function()
19836     {
19837         if(this.allowBlank){
19838             return;
19839         }
19840         
19841         var _this = this;
19842         
19843         this.fireEvent('valid', this);
19844         
19845         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19846         
19847         if(this.groupId){
19848             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19849         }
19850         
19851         if(label){
19852             label.markValid();
19853         }
19854         
19855         if(this.inputType == 'radio'){
19856             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19857                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19858                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19859             });
19860             
19861             return;
19862         }
19863         
19864         if(!this.groupId){
19865             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19866             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19867             return;
19868         }
19869         
19870         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19871             
19872         if(!group){
19873             return;
19874         }
19875         
19876         for(var i in group){
19877             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19878             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19879         }
19880     },
19881     
19882      /**
19883      * Mark this field as invalid
19884      * @param {String} msg The validation message
19885      */
19886     markInvalid : function(msg)
19887     {
19888         if(this.allowBlank){
19889             return;
19890         }
19891         
19892         var _this = this;
19893         
19894         this.fireEvent('invalid', this, msg);
19895         
19896         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19897         
19898         if(this.groupId){
19899             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19900         }
19901         
19902         if(label){
19903             label.markInvalid();
19904         }
19905             
19906         if(this.inputType == 'radio'){
19907             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19908                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19909                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19910             });
19911             
19912             return;
19913         }
19914         
19915         if(!this.groupId){
19916             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19917             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19918             return;
19919         }
19920         
19921         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19922         
19923         if(!group){
19924             return;
19925         }
19926         
19927         for(var i in group){
19928             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19929             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19930         }
19931         
19932     },
19933     
19934     disable : function()
19935     {
19936         if(this.inputType != 'radio'){
19937             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19938             return;
19939         }
19940         
19941         var _this = this;
19942         
19943         if(this.rendered){
19944             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19945                 _this.getActionEl().addClass(this.disabledClass);
19946                 e.dom.disabled = true;
19947             });
19948         }
19949         
19950         this.disabled = true;
19951         this.fireEvent("disable", this);
19952         return this;
19953     },
19954
19955     enable : function()
19956     {
19957         if(this.inputType != 'radio'){
19958             Roo.bootstrap.CheckBox.superclass.enable.call(this);
19959             return;
19960         }
19961         
19962         var _this = this;
19963         
19964         if(this.rendered){
19965             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19966                 _this.getActionEl().removeClass(this.disabledClass);
19967                 e.dom.disabled = false;
19968             });
19969         }
19970         
19971         this.disabled = false;
19972         this.fireEvent("enable", this);
19973         return this;
19974     }
19975
19976 });
19977
19978 Roo.apply(Roo.bootstrap.CheckBox, {
19979     
19980     groups: {},
19981     
19982      /**
19983     * register a CheckBox Group
19984     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19985     */
19986     register : function(checkbox)
19987     {
19988         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19989             this.groups[checkbox.groupId] = {};
19990         }
19991         
19992         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19993             return;
19994         }
19995         
19996         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19997         
19998     },
19999     /**
20000     * fetch a CheckBox Group based on the group ID
20001     * @param {string} the group ID
20002     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20003     */
20004     get: function(groupId) {
20005         if (typeof(this.groups[groupId]) == 'undefined') {
20006             return false;
20007         }
20008         
20009         return this.groups[groupId] ;
20010     }
20011     
20012     
20013 });
20014 /*
20015  * - LGPL
20016  *
20017  * Radio
20018  *
20019  *
20020  * not inline
20021  *<div class="radio">
20022   <label>
20023     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20024     Option one is this and that&mdash;be sure to include why it's great
20025   </label>
20026 </div>
20027  *
20028  *
20029  *inline
20030  *<span>
20031  *<label class="radio-inline">fieldLabel</label>
20032  *<label class="radio-inline">
20033   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20034 </label>
20035 <span>
20036  *
20037  *
20038  */
20039
20040 /**
20041  * @class Roo.bootstrap.Radio
20042  * @extends Roo.bootstrap.CheckBox
20043  * Bootstrap Radio class
20044
20045  * @constructor
20046  * Create a new Radio
20047  * @param {Object} config The config object
20048  */
20049
20050 Roo.bootstrap.Radio = function(config){
20051     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20052
20053 };
20054
20055 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20056
20057     inputType: 'radio',
20058     inputValue: '',
20059     valueOff: '',
20060
20061     getAutoCreate : function()
20062     {
20063         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20064         align = align || 'left'; // default...
20065
20066
20067
20068         var id = Roo.id();
20069
20070         var cfg = {
20071                 tag : this.inline ? 'span' : 'div',
20072                 cls : 'form-group',
20073                 cn : []
20074         };
20075
20076         var inline = this.inline ? ' radio-inline' : '';
20077
20078         var lbl = {
20079                 tag: 'label' ,
20080                 // does not need for, as we wrap the input with it..
20081                 'for' : id,
20082                 cls : 'control-label box-label' + inline,
20083                 cn : []
20084         };
20085         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20086
20087         var fieldLabel = {
20088             tag: 'label' ,
20089             //cls : 'control-label' + inline,
20090             html : this.fieldLabel,
20091             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20092         };
20093
20094         var input =  {
20095             tag: 'input',
20096             id : id,
20097             type : this.inputType,
20098             //value : (!this.checked) ? this.valueOff : this.inputValue,
20099             value : this.inputValue,
20100             cls : 'roo-radio',
20101             placeholder : this.placeholder || '' // ?? needed????
20102
20103         };
20104         if (this.weight) { // Validity check?
20105             input.cls += " radio-" + this.weight;
20106         }
20107         if (this.disabled) {
20108             input.disabled=true;
20109         }
20110
20111         if(this.checked){
20112             input.checked = this.checked;
20113         }
20114
20115         if (this.name) {
20116             input.name = this.name;
20117         }
20118
20119         if (this.size) {
20120             input.cls += ' input-' + this.size;
20121         }
20122
20123         //?? can span's inline have a width??
20124
20125         var settings=this;
20126         ['xs','sm','md','lg'].map(function(size){
20127             if (settings[size]) {
20128                 cfg.cls += ' col-' + size + '-' + settings[size];
20129             }
20130         });
20131
20132         var inputblock = input;
20133
20134         if (this.before || this.after) {
20135
20136             inputblock = {
20137                 cls : 'input-group',
20138                 tag : 'span',
20139                 cn :  []
20140             };
20141             if (this.before) {
20142                 inputblock.cn.push({
20143                     tag :'span',
20144                     cls : 'input-group-addon',
20145                     html : this.before
20146                 });
20147             }
20148             inputblock.cn.push(input);
20149             if (this.after) {
20150                 inputblock.cn.push({
20151                     tag :'span',
20152                     cls : 'input-group-addon',
20153                     html : this.after
20154                 });
20155             }
20156
20157         };
20158
20159
20160         if (this.fieldLabel && this.fieldLabel.length) {
20161             cfg.cn.push(fieldLabel);
20162         }
20163
20164         // normal bootstrap puts the input inside the label.
20165         // however with our styled version - it has to go after the input.
20166
20167         //lbl.cn.push(inputblock);
20168
20169         var lblwrap =  {
20170             tag: 'span',
20171             cls: 'radio' + inline,
20172             cn: [
20173                 inputblock,
20174                 lbl
20175             ]
20176         };
20177
20178         cfg.cn.push( lblwrap);
20179
20180         if(this.boxLabel){
20181             lbl.cn.push({
20182                 tag: 'span',
20183                 html: this.boxLabel
20184             })
20185         }
20186
20187
20188         return cfg;
20189
20190     },
20191
20192     initEvents : function()
20193     {
20194 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20195
20196         this.inputEl().on('click', this.onClick,  this);
20197         if (this.boxLabel) {
20198             //Roo.log('find label');
20199             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20200         }
20201
20202     },
20203
20204     inputEl: function ()
20205     {
20206         return this.el.select('input.roo-radio',true).first();
20207     },
20208     onClick : function()
20209     {
20210         Roo.log("click");
20211         this.setChecked(true);
20212     },
20213
20214     setChecked : function(state,suppressEvent)
20215     {
20216         if(state){
20217             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20218                 v.dom.checked = false;
20219             });
20220         }
20221         Roo.log(this.inputEl().dom);
20222         this.checked = state;
20223         this.inputEl().dom.checked = state;
20224
20225         if(suppressEvent !== true){
20226             this.fireEvent('check', this, state);
20227         }
20228
20229         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20230
20231     },
20232
20233     getGroupValue : function()
20234     {
20235         var value = '';
20236         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20237             if(v.dom.checked == true){
20238                 value = v.dom.value;
20239             }
20240         });
20241
20242         return value;
20243     },
20244
20245     /**
20246      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20247      * @return {Mixed} value The field value
20248      */
20249     getValue : function(){
20250         return this.getGroupValue();
20251     }
20252
20253 });
20254 //<script type="text/javascript">
20255
20256 /*
20257  * Based  Ext JS Library 1.1.1
20258  * Copyright(c) 2006-2007, Ext JS, LLC.
20259  * LGPL
20260  *
20261  */
20262  
20263 /**
20264  * @class Roo.HtmlEditorCore
20265  * @extends Roo.Component
20266  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20267  *
20268  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20269  */
20270
20271 Roo.HtmlEditorCore = function(config){
20272     
20273     
20274     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20275     
20276     
20277     this.addEvents({
20278         /**
20279          * @event initialize
20280          * Fires when the editor is fully initialized (including the iframe)
20281          * @param {Roo.HtmlEditorCore} this
20282          */
20283         initialize: true,
20284         /**
20285          * @event activate
20286          * Fires when the editor is first receives the focus. Any insertion must wait
20287          * until after this event.
20288          * @param {Roo.HtmlEditorCore} this
20289          */
20290         activate: true,
20291          /**
20292          * @event beforesync
20293          * Fires before the textarea is updated with content from the editor iframe. Return false
20294          * to cancel the sync.
20295          * @param {Roo.HtmlEditorCore} this
20296          * @param {String} html
20297          */
20298         beforesync: true,
20299          /**
20300          * @event beforepush
20301          * Fires before the iframe editor is updated with content from the textarea. Return false
20302          * to cancel the push.
20303          * @param {Roo.HtmlEditorCore} this
20304          * @param {String} html
20305          */
20306         beforepush: true,
20307          /**
20308          * @event sync
20309          * Fires when the textarea is updated with content from the editor iframe.
20310          * @param {Roo.HtmlEditorCore} this
20311          * @param {String} html
20312          */
20313         sync: true,
20314          /**
20315          * @event push
20316          * Fires when the iframe editor is updated with content from the textarea.
20317          * @param {Roo.HtmlEditorCore} this
20318          * @param {String} html
20319          */
20320         push: true,
20321         
20322         /**
20323          * @event editorevent
20324          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20325          * @param {Roo.HtmlEditorCore} this
20326          */
20327         editorevent: true
20328         
20329     });
20330     
20331     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20332     
20333     // defaults : white / black...
20334     this.applyBlacklists();
20335     
20336     
20337     
20338 };
20339
20340
20341 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20342
20343
20344      /**
20345      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20346      */
20347     
20348     owner : false,
20349     
20350      /**
20351      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20352      *                        Roo.resizable.
20353      */
20354     resizable : false,
20355      /**
20356      * @cfg {Number} height (in pixels)
20357      */   
20358     height: 300,
20359    /**
20360      * @cfg {Number} width (in pixels)
20361      */   
20362     width: 500,
20363     
20364     /**
20365      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20366      * 
20367      */
20368     stylesheets: false,
20369     
20370     // id of frame..
20371     frameId: false,
20372     
20373     // private properties
20374     validationEvent : false,
20375     deferHeight: true,
20376     initialized : false,
20377     activated : false,
20378     sourceEditMode : false,
20379     onFocus : Roo.emptyFn,
20380     iframePad:3,
20381     hideMode:'offsets',
20382     
20383     clearUp: true,
20384     
20385     // blacklist + whitelisted elements..
20386     black: false,
20387     white: false,
20388      
20389     
20390
20391     /**
20392      * Protected method that will not generally be called directly. It
20393      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20394      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20395      */
20396     getDocMarkup : function(){
20397         // body styles..
20398         var st = '';
20399         
20400         // inherit styels from page...?? 
20401         if (this.stylesheets === false) {
20402             
20403             Roo.get(document.head).select('style').each(function(node) {
20404                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20405             });
20406             
20407             Roo.get(document.head).select('link').each(function(node) { 
20408                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20409             });
20410             
20411         } else if (!this.stylesheets.length) {
20412                 // simple..
20413                 st = '<style type="text/css">' +
20414                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20415                    '</style>';
20416         } else { 
20417             
20418         }
20419         
20420         st +=  '<style type="text/css">' +
20421             'IMG { cursor: pointer } ' +
20422         '</style>';
20423
20424         
20425         return '<html><head>' + st  +
20426             //<style type="text/css">' +
20427             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20428             //'</style>' +
20429             ' </head><body class="roo-htmleditor-body"></body></html>';
20430     },
20431
20432     // private
20433     onRender : function(ct, position)
20434     {
20435         var _t = this;
20436         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20437         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20438         
20439         
20440         this.el.dom.style.border = '0 none';
20441         this.el.dom.setAttribute('tabIndex', -1);
20442         this.el.addClass('x-hidden hide');
20443         
20444         
20445         
20446         if(Roo.isIE){ // fix IE 1px bogus margin
20447             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20448         }
20449        
20450         
20451         this.frameId = Roo.id();
20452         
20453          
20454         
20455         var iframe = this.owner.wrap.createChild({
20456             tag: 'iframe',
20457             cls: 'form-control', // bootstrap..
20458             id: this.frameId,
20459             name: this.frameId,
20460             frameBorder : 'no',
20461             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20462         }, this.el
20463         );
20464         
20465         
20466         this.iframe = iframe.dom;
20467
20468          this.assignDocWin();
20469         
20470         this.doc.designMode = 'on';
20471        
20472         this.doc.open();
20473         this.doc.write(this.getDocMarkup());
20474         this.doc.close();
20475
20476         
20477         var task = { // must defer to wait for browser to be ready
20478             run : function(){
20479                 //console.log("run task?" + this.doc.readyState);
20480                 this.assignDocWin();
20481                 if(this.doc.body || this.doc.readyState == 'complete'){
20482                     try {
20483                         this.doc.designMode="on";
20484                     } catch (e) {
20485                         return;
20486                     }
20487                     Roo.TaskMgr.stop(task);
20488                     this.initEditor.defer(10, this);
20489                 }
20490             },
20491             interval : 10,
20492             duration: 10000,
20493             scope: this
20494         };
20495         Roo.TaskMgr.start(task);
20496
20497     },
20498
20499     // private
20500     onResize : function(w, h)
20501     {
20502          Roo.log('resize: ' +w + ',' + h );
20503         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20504         if(!this.iframe){
20505             return;
20506         }
20507         if(typeof w == 'number'){
20508             
20509             this.iframe.style.width = w + 'px';
20510         }
20511         if(typeof h == 'number'){
20512             
20513             this.iframe.style.height = h + 'px';
20514             if(this.doc){
20515                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20516             }
20517         }
20518         
20519     },
20520
20521     /**
20522      * Toggles the editor between standard and source edit mode.
20523      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20524      */
20525     toggleSourceEdit : function(sourceEditMode){
20526         
20527         this.sourceEditMode = sourceEditMode === true;
20528         
20529         if(this.sourceEditMode){
20530  
20531             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20532             
20533         }else{
20534             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20535             //this.iframe.className = '';
20536             this.deferFocus();
20537         }
20538         //this.setSize(this.owner.wrap.getSize());
20539         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20540     },
20541
20542     
20543   
20544
20545     /**
20546      * Protected method that will not generally be called directly. If you need/want
20547      * custom HTML cleanup, this is the method you should override.
20548      * @param {String} html The HTML to be cleaned
20549      * return {String} The cleaned HTML
20550      */
20551     cleanHtml : function(html){
20552         html = String(html);
20553         if(html.length > 5){
20554             if(Roo.isSafari){ // strip safari nonsense
20555                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20556             }
20557         }
20558         if(html == '&nbsp;'){
20559             html = '';
20560         }
20561         return html;
20562     },
20563
20564     /**
20565      * HTML Editor -> Textarea
20566      * Protected method that will not generally be called directly. Syncs the contents
20567      * of the editor iframe with the textarea.
20568      */
20569     syncValue : function(){
20570         if(this.initialized){
20571             var bd = (this.doc.body || this.doc.documentElement);
20572             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20573             var html = bd.innerHTML;
20574             if(Roo.isSafari){
20575                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20576                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20577                 if(m && m[1]){
20578                     html = '<div style="'+m[0]+'">' + html + '</div>';
20579                 }
20580             }
20581             html = this.cleanHtml(html);
20582             // fix up the special chars.. normaly like back quotes in word...
20583             // however we do not want to do this with chinese..
20584             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20585                 var cc = b.charCodeAt();
20586                 if (
20587                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20588                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20589                     (cc >= 0xf900 && cc < 0xfb00 )
20590                 ) {
20591                         return b;
20592                 }
20593                 return "&#"+cc+";" 
20594             });
20595             if(this.owner.fireEvent('beforesync', this, html) !== false){
20596                 this.el.dom.value = html;
20597                 this.owner.fireEvent('sync', this, html);
20598             }
20599         }
20600     },
20601
20602     /**
20603      * Protected method that will not generally be called directly. Pushes the value of the textarea
20604      * into the iframe editor.
20605      */
20606     pushValue : function(){
20607         if(this.initialized){
20608             var v = this.el.dom.value.trim();
20609             
20610 //            if(v.length < 1){
20611 //                v = '&#160;';
20612 //            }
20613             
20614             if(this.owner.fireEvent('beforepush', this, v) !== false){
20615                 var d = (this.doc.body || this.doc.documentElement);
20616                 d.innerHTML = v;
20617                 this.cleanUpPaste();
20618                 this.el.dom.value = d.innerHTML;
20619                 this.owner.fireEvent('push', this, v);
20620             }
20621         }
20622     },
20623
20624     // private
20625     deferFocus : function(){
20626         this.focus.defer(10, this);
20627     },
20628
20629     // doc'ed in Field
20630     focus : function(){
20631         if(this.win && !this.sourceEditMode){
20632             this.win.focus();
20633         }else{
20634             this.el.focus();
20635         }
20636     },
20637     
20638     assignDocWin: function()
20639     {
20640         var iframe = this.iframe;
20641         
20642          if(Roo.isIE){
20643             this.doc = iframe.contentWindow.document;
20644             this.win = iframe.contentWindow;
20645         } else {
20646 //            if (!Roo.get(this.frameId)) {
20647 //                return;
20648 //            }
20649 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20650 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20651             
20652             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20653                 return;
20654             }
20655             
20656             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20657             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20658         }
20659     },
20660     
20661     // private
20662     initEditor : function(){
20663         //console.log("INIT EDITOR");
20664         this.assignDocWin();
20665         
20666         
20667         
20668         this.doc.designMode="on";
20669         this.doc.open();
20670         this.doc.write(this.getDocMarkup());
20671         this.doc.close();
20672         
20673         var dbody = (this.doc.body || this.doc.documentElement);
20674         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20675         // this copies styles from the containing element into thsi one..
20676         // not sure why we need all of this..
20677         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20678         
20679         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20680         //ss['background-attachment'] = 'fixed'; // w3c
20681         dbody.bgProperties = 'fixed'; // ie
20682         //Roo.DomHelper.applyStyles(dbody, ss);
20683         Roo.EventManager.on(this.doc, {
20684             //'mousedown': this.onEditorEvent,
20685             'mouseup': this.onEditorEvent,
20686             'dblclick': this.onEditorEvent,
20687             'click': this.onEditorEvent,
20688             'keyup': this.onEditorEvent,
20689             buffer:100,
20690             scope: this
20691         });
20692         if(Roo.isGecko){
20693             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20694         }
20695         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20696             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20697         }
20698         this.initialized = true;
20699
20700         this.owner.fireEvent('initialize', this);
20701         this.pushValue();
20702     },
20703
20704     // private
20705     onDestroy : function(){
20706         
20707         
20708         
20709         if(this.rendered){
20710             
20711             //for (var i =0; i < this.toolbars.length;i++) {
20712             //    // fixme - ask toolbars for heights?
20713             //    this.toolbars[i].onDestroy();
20714            // }
20715             
20716             //this.wrap.dom.innerHTML = '';
20717             //this.wrap.remove();
20718         }
20719     },
20720
20721     // private
20722     onFirstFocus : function(){
20723         
20724         this.assignDocWin();
20725         
20726         
20727         this.activated = true;
20728          
20729     
20730         if(Roo.isGecko){ // prevent silly gecko errors
20731             this.win.focus();
20732             var s = this.win.getSelection();
20733             if(!s.focusNode || s.focusNode.nodeType != 3){
20734                 var r = s.getRangeAt(0);
20735                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20736                 r.collapse(true);
20737                 this.deferFocus();
20738             }
20739             try{
20740                 this.execCmd('useCSS', true);
20741                 this.execCmd('styleWithCSS', false);
20742             }catch(e){}
20743         }
20744         this.owner.fireEvent('activate', this);
20745     },
20746
20747     // private
20748     adjustFont: function(btn){
20749         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20750         //if(Roo.isSafari){ // safari
20751         //    adjust *= 2;
20752        // }
20753         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20754         if(Roo.isSafari){ // safari
20755             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20756             v =  (v < 10) ? 10 : v;
20757             v =  (v > 48) ? 48 : v;
20758             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20759             
20760         }
20761         
20762         
20763         v = Math.max(1, v+adjust);
20764         
20765         this.execCmd('FontSize', v  );
20766     },
20767
20768     onEditorEvent : function(e)
20769     {
20770         this.owner.fireEvent('editorevent', this, e);
20771       //  this.updateToolbar();
20772         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20773     },
20774
20775     insertTag : function(tg)
20776     {
20777         // could be a bit smarter... -> wrap the current selected tRoo..
20778         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20779             
20780             range = this.createRange(this.getSelection());
20781             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20782             wrappingNode.appendChild(range.extractContents());
20783             range.insertNode(wrappingNode);
20784
20785             return;
20786             
20787             
20788             
20789         }
20790         this.execCmd("formatblock",   tg);
20791         
20792     },
20793     
20794     insertText : function(txt)
20795     {
20796         
20797         
20798         var range = this.createRange();
20799         range.deleteContents();
20800                //alert(Sender.getAttribute('label'));
20801                
20802         range.insertNode(this.doc.createTextNode(txt));
20803     } ,
20804     
20805      
20806
20807     /**
20808      * Executes a Midas editor command on the editor document and performs necessary focus and
20809      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20810      * @param {String} cmd The Midas command
20811      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20812      */
20813     relayCmd : function(cmd, value){
20814         this.win.focus();
20815         this.execCmd(cmd, value);
20816         this.owner.fireEvent('editorevent', this);
20817         //this.updateToolbar();
20818         this.owner.deferFocus();
20819     },
20820
20821     /**
20822      * Executes a Midas editor command directly on the editor document.
20823      * For visual commands, you should use {@link #relayCmd} instead.
20824      * <b>This should only be called after the editor is initialized.</b>
20825      * @param {String} cmd The Midas command
20826      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20827      */
20828     execCmd : function(cmd, value){
20829         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20830         this.syncValue();
20831     },
20832  
20833  
20834    
20835     /**
20836      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20837      * to insert tRoo.
20838      * @param {String} text | dom node.. 
20839      */
20840     insertAtCursor : function(text)
20841     {
20842         
20843         
20844         
20845         if(!this.activated){
20846             return;
20847         }
20848         /*
20849         if(Roo.isIE){
20850             this.win.focus();
20851             var r = this.doc.selection.createRange();
20852             if(r){
20853                 r.collapse(true);
20854                 r.pasteHTML(text);
20855                 this.syncValue();
20856                 this.deferFocus();
20857             
20858             }
20859             return;
20860         }
20861         */
20862         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20863             this.win.focus();
20864             
20865             
20866             // from jquery ui (MIT licenced)
20867             var range, node;
20868             var win = this.win;
20869             
20870             if (win.getSelection && win.getSelection().getRangeAt) {
20871                 range = win.getSelection().getRangeAt(0);
20872                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20873                 range.insertNode(node);
20874             } else if (win.document.selection && win.document.selection.createRange) {
20875                 // no firefox support
20876                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20877                 win.document.selection.createRange().pasteHTML(txt);
20878             } else {
20879                 // no firefox support
20880                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20881                 this.execCmd('InsertHTML', txt);
20882             } 
20883             
20884             this.syncValue();
20885             
20886             this.deferFocus();
20887         }
20888     },
20889  // private
20890     mozKeyPress : function(e){
20891         if(e.ctrlKey){
20892             var c = e.getCharCode(), cmd;
20893           
20894             if(c > 0){
20895                 c = String.fromCharCode(c).toLowerCase();
20896                 switch(c){
20897                     case 'b':
20898                         cmd = 'bold';
20899                         break;
20900                     case 'i':
20901                         cmd = 'italic';
20902                         break;
20903                     
20904                     case 'u':
20905                         cmd = 'underline';
20906                         break;
20907                     
20908                     case 'v':
20909                         this.cleanUpPaste.defer(100, this);
20910                         return;
20911                         
20912                 }
20913                 if(cmd){
20914                     this.win.focus();
20915                     this.execCmd(cmd);
20916                     this.deferFocus();
20917                     e.preventDefault();
20918                 }
20919                 
20920             }
20921         }
20922     },
20923
20924     // private
20925     fixKeys : function(){ // load time branching for fastest keydown performance
20926         if(Roo.isIE){
20927             return function(e){
20928                 var k = e.getKey(), r;
20929                 if(k == e.TAB){
20930                     e.stopEvent();
20931                     r = this.doc.selection.createRange();
20932                     if(r){
20933                         r.collapse(true);
20934                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20935                         this.deferFocus();
20936                     }
20937                     return;
20938                 }
20939                 
20940                 if(k == e.ENTER){
20941                     r = this.doc.selection.createRange();
20942                     if(r){
20943                         var target = r.parentElement();
20944                         if(!target || target.tagName.toLowerCase() != 'li'){
20945                             e.stopEvent();
20946                             r.pasteHTML('<br />');
20947                             r.collapse(false);
20948                             r.select();
20949                         }
20950                     }
20951                 }
20952                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20953                     this.cleanUpPaste.defer(100, this);
20954                     return;
20955                 }
20956                 
20957                 
20958             };
20959         }else if(Roo.isOpera){
20960             return function(e){
20961                 var k = e.getKey();
20962                 if(k == e.TAB){
20963                     e.stopEvent();
20964                     this.win.focus();
20965                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20966                     this.deferFocus();
20967                 }
20968                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20969                     this.cleanUpPaste.defer(100, this);
20970                     return;
20971                 }
20972                 
20973             };
20974         }else if(Roo.isSafari){
20975             return function(e){
20976                 var k = e.getKey();
20977                 
20978                 if(k == e.TAB){
20979                     e.stopEvent();
20980                     this.execCmd('InsertText','\t');
20981                     this.deferFocus();
20982                     return;
20983                 }
20984                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20985                     this.cleanUpPaste.defer(100, this);
20986                     return;
20987                 }
20988                 
20989              };
20990         }
20991     }(),
20992     
20993     getAllAncestors: function()
20994     {
20995         var p = this.getSelectedNode();
20996         var a = [];
20997         if (!p) {
20998             a.push(p); // push blank onto stack..
20999             p = this.getParentElement();
21000         }
21001         
21002         
21003         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21004             a.push(p);
21005             p = p.parentNode;
21006         }
21007         a.push(this.doc.body);
21008         return a;
21009     },
21010     lastSel : false,
21011     lastSelNode : false,
21012     
21013     
21014     getSelection : function() 
21015     {
21016         this.assignDocWin();
21017         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21018     },
21019     
21020     getSelectedNode: function() 
21021     {
21022         // this may only work on Gecko!!!
21023         
21024         // should we cache this!!!!
21025         
21026         
21027         
21028          
21029         var range = this.createRange(this.getSelection()).cloneRange();
21030         
21031         if (Roo.isIE) {
21032             var parent = range.parentElement();
21033             while (true) {
21034                 var testRange = range.duplicate();
21035                 testRange.moveToElementText(parent);
21036                 if (testRange.inRange(range)) {
21037                     break;
21038                 }
21039                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21040                     break;
21041                 }
21042                 parent = parent.parentElement;
21043             }
21044             return parent;
21045         }
21046         
21047         // is ancestor a text element.
21048         var ac =  range.commonAncestorContainer;
21049         if (ac.nodeType == 3) {
21050             ac = ac.parentNode;
21051         }
21052         
21053         var ar = ac.childNodes;
21054          
21055         var nodes = [];
21056         var other_nodes = [];
21057         var has_other_nodes = false;
21058         for (var i=0;i<ar.length;i++) {
21059             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21060                 continue;
21061             }
21062             // fullly contained node.
21063             
21064             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21065                 nodes.push(ar[i]);
21066                 continue;
21067             }
21068             
21069             // probably selected..
21070             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21071                 other_nodes.push(ar[i]);
21072                 continue;
21073             }
21074             // outer..
21075             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21076                 continue;
21077             }
21078             
21079             
21080             has_other_nodes = true;
21081         }
21082         if (!nodes.length && other_nodes.length) {
21083             nodes= other_nodes;
21084         }
21085         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21086             return false;
21087         }
21088         
21089         return nodes[0];
21090     },
21091     createRange: function(sel)
21092     {
21093         // this has strange effects when using with 
21094         // top toolbar - not sure if it's a great idea.
21095         //this.editor.contentWindow.focus();
21096         if (typeof sel != "undefined") {
21097             try {
21098                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21099             } catch(e) {
21100                 return this.doc.createRange();
21101             }
21102         } else {
21103             return this.doc.createRange();
21104         }
21105     },
21106     getParentElement: function()
21107     {
21108         
21109         this.assignDocWin();
21110         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21111         
21112         var range = this.createRange(sel);
21113          
21114         try {
21115             var p = range.commonAncestorContainer;
21116             while (p.nodeType == 3) { // text node
21117                 p = p.parentNode;
21118             }
21119             return p;
21120         } catch (e) {
21121             return null;
21122         }
21123     
21124     },
21125     /***
21126      *
21127      * Range intersection.. the hard stuff...
21128      *  '-1' = before
21129      *  '0' = hits..
21130      *  '1' = after.
21131      *         [ -- selected range --- ]
21132      *   [fail]                        [fail]
21133      *
21134      *    basically..
21135      *      if end is before start or  hits it. fail.
21136      *      if start is after end or hits it fail.
21137      *
21138      *   if either hits (but other is outside. - then it's not 
21139      *   
21140      *    
21141      **/
21142     
21143     
21144     // @see http://www.thismuchiknow.co.uk/?p=64.
21145     rangeIntersectsNode : function(range, node)
21146     {
21147         var nodeRange = node.ownerDocument.createRange();
21148         try {
21149             nodeRange.selectNode(node);
21150         } catch (e) {
21151             nodeRange.selectNodeContents(node);
21152         }
21153     
21154         var rangeStartRange = range.cloneRange();
21155         rangeStartRange.collapse(true);
21156     
21157         var rangeEndRange = range.cloneRange();
21158         rangeEndRange.collapse(false);
21159     
21160         var nodeStartRange = nodeRange.cloneRange();
21161         nodeStartRange.collapse(true);
21162     
21163         var nodeEndRange = nodeRange.cloneRange();
21164         nodeEndRange.collapse(false);
21165     
21166         return rangeStartRange.compareBoundaryPoints(
21167                  Range.START_TO_START, nodeEndRange) == -1 &&
21168                rangeEndRange.compareBoundaryPoints(
21169                  Range.START_TO_START, nodeStartRange) == 1;
21170         
21171          
21172     },
21173     rangeCompareNode : function(range, node)
21174     {
21175         var nodeRange = node.ownerDocument.createRange();
21176         try {
21177             nodeRange.selectNode(node);
21178         } catch (e) {
21179             nodeRange.selectNodeContents(node);
21180         }
21181         
21182         
21183         range.collapse(true);
21184     
21185         nodeRange.collapse(true);
21186      
21187         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21188         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21189          
21190         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21191         
21192         var nodeIsBefore   =  ss == 1;
21193         var nodeIsAfter    = ee == -1;
21194         
21195         if (nodeIsBefore && nodeIsAfter) {
21196             return 0; // outer
21197         }
21198         if (!nodeIsBefore && nodeIsAfter) {
21199             return 1; //right trailed.
21200         }
21201         
21202         if (nodeIsBefore && !nodeIsAfter) {
21203             return 2;  // left trailed.
21204         }
21205         // fully contined.
21206         return 3;
21207     },
21208
21209     // private? - in a new class?
21210     cleanUpPaste :  function()
21211     {
21212         // cleans up the whole document..
21213         Roo.log('cleanuppaste');
21214         
21215         this.cleanUpChildren(this.doc.body);
21216         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21217         if (clean != this.doc.body.innerHTML) {
21218             this.doc.body.innerHTML = clean;
21219         }
21220         
21221     },
21222     
21223     cleanWordChars : function(input) {// change the chars to hex code
21224         var he = Roo.HtmlEditorCore;
21225         
21226         var output = input;
21227         Roo.each(he.swapCodes, function(sw) { 
21228             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21229             
21230             output = output.replace(swapper, sw[1]);
21231         });
21232         
21233         return output;
21234     },
21235     
21236     
21237     cleanUpChildren : function (n)
21238     {
21239         if (!n.childNodes.length) {
21240             return;
21241         }
21242         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21243            this.cleanUpChild(n.childNodes[i]);
21244         }
21245     },
21246     
21247     
21248         
21249     
21250     cleanUpChild : function (node)
21251     {
21252         var ed = this;
21253         //console.log(node);
21254         if (node.nodeName == "#text") {
21255             // clean up silly Windows -- stuff?
21256             return; 
21257         }
21258         if (node.nodeName == "#comment") {
21259             node.parentNode.removeChild(node);
21260             // clean up silly Windows -- stuff?
21261             return; 
21262         }
21263         var lcname = node.tagName.toLowerCase();
21264         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21265         // whitelist of tags..
21266         
21267         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21268             // remove node.
21269             node.parentNode.removeChild(node);
21270             return;
21271             
21272         }
21273         
21274         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21275         
21276         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21277         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21278         
21279         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21280         //    remove_keep_children = true;
21281         //}
21282         
21283         if (remove_keep_children) {
21284             this.cleanUpChildren(node);
21285             // inserts everything just before this node...
21286             while (node.childNodes.length) {
21287                 var cn = node.childNodes[0];
21288                 node.removeChild(cn);
21289                 node.parentNode.insertBefore(cn, node);
21290             }
21291             node.parentNode.removeChild(node);
21292             return;
21293         }
21294         
21295         if (!node.attributes || !node.attributes.length) {
21296             this.cleanUpChildren(node);
21297             return;
21298         }
21299         
21300         function cleanAttr(n,v)
21301         {
21302             
21303             if (v.match(/^\./) || v.match(/^\//)) {
21304                 return;
21305             }
21306             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21307                 return;
21308             }
21309             if (v.match(/^#/)) {
21310                 return;
21311             }
21312 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21313             node.removeAttribute(n);
21314             
21315         }
21316         
21317         var cwhite = this.cwhite;
21318         var cblack = this.cblack;
21319             
21320         function cleanStyle(n,v)
21321         {
21322             if (v.match(/expression/)) { //XSS?? should we even bother..
21323                 node.removeAttribute(n);
21324                 return;
21325             }
21326             
21327             var parts = v.split(/;/);
21328             var clean = [];
21329             
21330             Roo.each(parts, function(p) {
21331                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21332                 if (!p.length) {
21333                     return true;
21334                 }
21335                 var l = p.split(':').shift().replace(/\s+/g,'');
21336                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21337                 
21338                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21339 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21340                     //node.removeAttribute(n);
21341                     return true;
21342                 }
21343                 //Roo.log()
21344                 // only allow 'c whitelisted system attributes'
21345                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21346 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21347                     //node.removeAttribute(n);
21348                     return true;
21349                 }
21350                 
21351                 
21352                  
21353                 
21354                 clean.push(p);
21355                 return true;
21356             });
21357             if (clean.length) { 
21358                 node.setAttribute(n, clean.join(';'));
21359             } else {
21360                 node.removeAttribute(n);
21361             }
21362             
21363         }
21364         
21365         
21366         for (var i = node.attributes.length-1; i > -1 ; i--) {
21367             var a = node.attributes[i];
21368             //console.log(a);
21369             
21370             if (a.name.toLowerCase().substr(0,2)=='on')  {
21371                 node.removeAttribute(a.name);
21372                 continue;
21373             }
21374             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21375                 node.removeAttribute(a.name);
21376                 continue;
21377             }
21378             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21379                 cleanAttr(a.name,a.value); // fixme..
21380                 continue;
21381             }
21382             if (a.name == 'style') {
21383                 cleanStyle(a.name,a.value);
21384                 continue;
21385             }
21386             /// clean up MS crap..
21387             // tecnically this should be a list of valid class'es..
21388             
21389             
21390             if (a.name == 'class') {
21391                 if (a.value.match(/^Mso/)) {
21392                     node.className = '';
21393                 }
21394                 
21395                 if (a.value.match(/body/)) {
21396                     node.className = '';
21397                 }
21398                 continue;
21399             }
21400             
21401             // style cleanup!?
21402             // class cleanup?
21403             
21404         }
21405         
21406         
21407         this.cleanUpChildren(node);
21408         
21409         
21410     },
21411     
21412     /**
21413      * Clean up MS wordisms...
21414      */
21415     cleanWord : function(node)
21416     {
21417         
21418         
21419         if (!node) {
21420             this.cleanWord(this.doc.body);
21421             return;
21422         }
21423         if (node.nodeName == "#text") {
21424             // clean up silly Windows -- stuff?
21425             return; 
21426         }
21427         if (node.nodeName == "#comment") {
21428             node.parentNode.removeChild(node);
21429             // clean up silly Windows -- stuff?
21430             return; 
21431         }
21432         
21433         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21434             node.parentNode.removeChild(node);
21435             return;
21436         }
21437         
21438         // remove - but keep children..
21439         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21440             while (node.childNodes.length) {
21441                 var cn = node.childNodes[0];
21442                 node.removeChild(cn);
21443                 node.parentNode.insertBefore(cn, node);
21444             }
21445             node.parentNode.removeChild(node);
21446             this.iterateChildren(node, this.cleanWord);
21447             return;
21448         }
21449         // clean styles
21450         if (node.className.length) {
21451             
21452             var cn = node.className.split(/\W+/);
21453             var cna = [];
21454             Roo.each(cn, function(cls) {
21455                 if (cls.match(/Mso[a-zA-Z]+/)) {
21456                     return;
21457                 }
21458                 cna.push(cls);
21459             });
21460             node.className = cna.length ? cna.join(' ') : '';
21461             if (!cna.length) {
21462                 node.removeAttribute("class");
21463             }
21464         }
21465         
21466         if (node.hasAttribute("lang")) {
21467             node.removeAttribute("lang");
21468         }
21469         
21470         if (node.hasAttribute("style")) {
21471             
21472             var styles = node.getAttribute("style").split(";");
21473             var nstyle = [];
21474             Roo.each(styles, function(s) {
21475                 if (!s.match(/:/)) {
21476                     return;
21477                 }
21478                 var kv = s.split(":");
21479                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21480                     return;
21481                 }
21482                 // what ever is left... we allow.
21483                 nstyle.push(s);
21484             });
21485             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21486             if (!nstyle.length) {
21487                 node.removeAttribute('style');
21488             }
21489         }
21490         this.iterateChildren(node, this.cleanWord);
21491         
21492         
21493         
21494     },
21495     /**
21496      * iterateChildren of a Node, calling fn each time, using this as the scole..
21497      * @param {DomNode} node node to iterate children of.
21498      * @param {Function} fn method of this class to call on each item.
21499      */
21500     iterateChildren : function(node, fn)
21501     {
21502         if (!node.childNodes.length) {
21503                 return;
21504         }
21505         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21506            fn.call(this, node.childNodes[i])
21507         }
21508     },
21509     
21510     
21511     /**
21512      * cleanTableWidths.
21513      *
21514      * Quite often pasting from word etc.. results in tables with column and widths.
21515      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21516      *
21517      */
21518     cleanTableWidths : function(node)
21519     {
21520          
21521          
21522         if (!node) {
21523             this.cleanTableWidths(this.doc.body);
21524             return;
21525         }
21526         
21527         // ignore list...
21528         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21529             return; 
21530         }
21531         Roo.log(node.tagName);
21532         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21533             this.iterateChildren(node, this.cleanTableWidths);
21534             return;
21535         }
21536         if (node.hasAttribute('width')) {
21537             node.removeAttribute('width');
21538         }
21539         
21540          
21541         if (node.hasAttribute("style")) {
21542             // pretty basic...
21543             
21544             var styles = node.getAttribute("style").split(";");
21545             var nstyle = [];
21546             Roo.each(styles, function(s) {
21547                 if (!s.match(/:/)) {
21548                     return;
21549                 }
21550                 var kv = s.split(":");
21551                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21552                     return;
21553                 }
21554                 // what ever is left... we allow.
21555                 nstyle.push(s);
21556             });
21557             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21558             if (!nstyle.length) {
21559                 node.removeAttribute('style');
21560             }
21561         }
21562         
21563         this.iterateChildren(node, this.cleanTableWidths);
21564         
21565         
21566     },
21567     
21568     
21569     
21570     
21571     domToHTML : function(currentElement, depth, nopadtext) {
21572         
21573         depth = depth || 0;
21574         nopadtext = nopadtext || false;
21575     
21576         if (!currentElement) {
21577             return this.domToHTML(this.doc.body);
21578         }
21579         
21580         //Roo.log(currentElement);
21581         var j;
21582         var allText = false;
21583         var nodeName = currentElement.nodeName;
21584         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21585         
21586         if  (nodeName == '#text') {
21587             
21588             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21589         }
21590         
21591         
21592         var ret = '';
21593         if (nodeName != 'BODY') {
21594              
21595             var i = 0;
21596             // Prints the node tagName, such as <A>, <IMG>, etc
21597             if (tagName) {
21598                 var attr = [];
21599                 for(i = 0; i < currentElement.attributes.length;i++) {
21600                     // quoting?
21601                     var aname = currentElement.attributes.item(i).name;
21602                     if (!currentElement.attributes.item(i).value.length) {
21603                         continue;
21604                     }
21605                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21606                 }
21607                 
21608                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21609             } 
21610             else {
21611                 
21612                 // eack
21613             }
21614         } else {
21615             tagName = false;
21616         }
21617         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21618             return ret;
21619         }
21620         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21621             nopadtext = true;
21622         }
21623         
21624         
21625         // Traverse the tree
21626         i = 0;
21627         var currentElementChild = currentElement.childNodes.item(i);
21628         var allText = true;
21629         var innerHTML  = '';
21630         lastnode = '';
21631         while (currentElementChild) {
21632             // Formatting code (indent the tree so it looks nice on the screen)
21633             var nopad = nopadtext;
21634             if (lastnode == 'SPAN') {
21635                 nopad  = true;
21636             }
21637             // text
21638             if  (currentElementChild.nodeName == '#text') {
21639                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21640                 toadd = nopadtext ? toadd : toadd.trim();
21641                 if (!nopad && toadd.length > 80) {
21642                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21643                 }
21644                 innerHTML  += toadd;
21645                 
21646                 i++;
21647                 currentElementChild = currentElement.childNodes.item(i);
21648                 lastNode = '';
21649                 continue;
21650             }
21651             allText = false;
21652             
21653             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21654                 
21655             // Recursively traverse the tree structure of the child node
21656             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21657             lastnode = currentElementChild.nodeName;
21658             i++;
21659             currentElementChild=currentElement.childNodes.item(i);
21660         }
21661         
21662         ret += innerHTML;
21663         
21664         if (!allText) {
21665                 // The remaining code is mostly for formatting the tree
21666             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21667         }
21668         
21669         
21670         if (tagName) {
21671             ret+= "</"+tagName+">";
21672         }
21673         return ret;
21674         
21675     },
21676         
21677     applyBlacklists : function()
21678     {
21679         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21680         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21681         
21682         this.white = [];
21683         this.black = [];
21684         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21685             if (b.indexOf(tag) > -1) {
21686                 return;
21687             }
21688             this.white.push(tag);
21689             
21690         }, this);
21691         
21692         Roo.each(w, function(tag) {
21693             if (b.indexOf(tag) > -1) {
21694                 return;
21695             }
21696             if (this.white.indexOf(tag) > -1) {
21697                 return;
21698             }
21699             this.white.push(tag);
21700             
21701         }, this);
21702         
21703         
21704         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21705             if (w.indexOf(tag) > -1) {
21706                 return;
21707             }
21708             this.black.push(tag);
21709             
21710         }, this);
21711         
21712         Roo.each(b, function(tag) {
21713             if (w.indexOf(tag) > -1) {
21714                 return;
21715             }
21716             if (this.black.indexOf(tag) > -1) {
21717                 return;
21718             }
21719             this.black.push(tag);
21720             
21721         }, this);
21722         
21723         
21724         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21725         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21726         
21727         this.cwhite = [];
21728         this.cblack = [];
21729         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21730             if (b.indexOf(tag) > -1) {
21731                 return;
21732             }
21733             this.cwhite.push(tag);
21734             
21735         }, this);
21736         
21737         Roo.each(w, function(tag) {
21738             if (b.indexOf(tag) > -1) {
21739                 return;
21740             }
21741             if (this.cwhite.indexOf(tag) > -1) {
21742                 return;
21743             }
21744             this.cwhite.push(tag);
21745             
21746         }, this);
21747         
21748         
21749         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21750             if (w.indexOf(tag) > -1) {
21751                 return;
21752             }
21753             this.cblack.push(tag);
21754             
21755         }, this);
21756         
21757         Roo.each(b, function(tag) {
21758             if (w.indexOf(tag) > -1) {
21759                 return;
21760             }
21761             if (this.cblack.indexOf(tag) > -1) {
21762                 return;
21763             }
21764             this.cblack.push(tag);
21765             
21766         }, this);
21767     },
21768     
21769     setStylesheets : function(stylesheets)
21770     {
21771         if(typeof(stylesheets) == 'string'){
21772             Roo.get(this.iframe.contentDocument.head).createChild({
21773                 tag : 'link',
21774                 rel : 'stylesheet',
21775                 type : 'text/css',
21776                 href : stylesheets
21777             });
21778             
21779             return;
21780         }
21781         var _this = this;
21782      
21783         Roo.each(stylesheets, function(s) {
21784             if(!s.length){
21785                 return;
21786             }
21787             
21788             Roo.get(_this.iframe.contentDocument.head).createChild({
21789                 tag : 'link',
21790                 rel : 'stylesheet',
21791                 type : 'text/css',
21792                 href : s
21793             });
21794         });
21795
21796         
21797     },
21798     
21799     removeStylesheets : function()
21800     {
21801         var _this = this;
21802         
21803         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21804             s.remove();
21805         });
21806     }
21807     
21808     // hide stuff that is not compatible
21809     /**
21810      * @event blur
21811      * @hide
21812      */
21813     /**
21814      * @event change
21815      * @hide
21816      */
21817     /**
21818      * @event focus
21819      * @hide
21820      */
21821     /**
21822      * @event specialkey
21823      * @hide
21824      */
21825     /**
21826      * @cfg {String} fieldClass @hide
21827      */
21828     /**
21829      * @cfg {String} focusClass @hide
21830      */
21831     /**
21832      * @cfg {String} autoCreate @hide
21833      */
21834     /**
21835      * @cfg {String} inputType @hide
21836      */
21837     /**
21838      * @cfg {String} invalidClass @hide
21839      */
21840     /**
21841      * @cfg {String} invalidText @hide
21842      */
21843     /**
21844      * @cfg {String} msgFx @hide
21845      */
21846     /**
21847      * @cfg {String} validateOnBlur @hide
21848      */
21849 });
21850
21851 Roo.HtmlEditorCore.white = [
21852         'area', 'br', 'img', 'input', 'hr', 'wbr',
21853         
21854        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21855        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21856        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21857        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21858        'table',   'ul',         'xmp', 
21859        
21860        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21861       'thead',   'tr', 
21862      
21863       'dir', 'menu', 'ol', 'ul', 'dl',
21864        
21865       'embed',  'object'
21866 ];
21867
21868
21869 Roo.HtmlEditorCore.black = [
21870     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21871         'applet', // 
21872         'base',   'basefont', 'bgsound', 'blink',  'body', 
21873         'frame',  'frameset', 'head',    'html',   'ilayer', 
21874         'iframe', 'layer',  'link',     'meta',    'object',   
21875         'script', 'style' ,'title',  'xml' // clean later..
21876 ];
21877 Roo.HtmlEditorCore.clean = [
21878     'script', 'style', 'title', 'xml'
21879 ];
21880 Roo.HtmlEditorCore.remove = [
21881     'font'
21882 ];
21883 // attributes..
21884
21885 Roo.HtmlEditorCore.ablack = [
21886     'on'
21887 ];
21888     
21889 Roo.HtmlEditorCore.aclean = [ 
21890     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21891 ];
21892
21893 // protocols..
21894 Roo.HtmlEditorCore.pwhite= [
21895         'http',  'https',  'mailto'
21896 ];
21897
21898 // white listed style attributes.
21899 Roo.HtmlEditorCore.cwhite= [
21900       //  'text-align', /// default is to allow most things..
21901       
21902          
21903 //        'font-size'//??
21904 ];
21905
21906 // black listed style attributes.
21907 Roo.HtmlEditorCore.cblack= [
21908       //  'font-size' -- this can be set by the project 
21909 ];
21910
21911
21912 Roo.HtmlEditorCore.swapCodes   =[ 
21913     [    8211, "--" ], 
21914     [    8212, "--" ], 
21915     [    8216,  "'" ],  
21916     [    8217, "'" ],  
21917     [    8220, '"' ],  
21918     [    8221, '"' ],  
21919     [    8226, "*" ],  
21920     [    8230, "..." ]
21921 ]; 
21922
21923     /*
21924  * - LGPL
21925  *
21926  * HtmlEditor
21927  * 
21928  */
21929
21930 /**
21931  * @class Roo.bootstrap.HtmlEditor
21932  * @extends Roo.bootstrap.TextArea
21933  * Bootstrap HtmlEditor class
21934
21935  * @constructor
21936  * Create a new HtmlEditor
21937  * @param {Object} config The config object
21938  */
21939
21940 Roo.bootstrap.HtmlEditor = function(config){
21941     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21942     if (!this.toolbars) {
21943         this.toolbars = [];
21944     }
21945     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21946     this.addEvents({
21947             /**
21948              * @event initialize
21949              * Fires when the editor is fully initialized (including the iframe)
21950              * @param {HtmlEditor} this
21951              */
21952             initialize: true,
21953             /**
21954              * @event activate
21955              * Fires when the editor is first receives the focus. Any insertion must wait
21956              * until after this event.
21957              * @param {HtmlEditor} this
21958              */
21959             activate: true,
21960              /**
21961              * @event beforesync
21962              * Fires before the textarea is updated with content from the editor iframe. Return false
21963              * to cancel the sync.
21964              * @param {HtmlEditor} this
21965              * @param {String} html
21966              */
21967             beforesync: true,
21968              /**
21969              * @event beforepush
21970              * Fires before the iframe editor is updated with content from the textarea. Return false
21971              * to cancel the push.
21972              * @param {HtmlEditor} this
21973              * @param {String} html
21974              */
21975             beforepush: true,
21976              /**
21977              * @event sync
21978              * Fires when the textarea is updated with content from the editor iframe.
21979              * @param {HtmlEditor} this
21980              * @param {String} html
21981              */
21982             sync: true,
21983              /**
21984              * @event push
21985              * Fires when the iframe editor is updated with content from the textarea.
21986              * @param {HtmlEditor} this
21987              * @param {String} html
21988              */
21989             push: true,
21990              /**
21991              * @event editmodechange
21992              * Fires when the editor switches edit modes
21993              * @param {HtmlEditor} this
21994              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21995              */
21996             editmodechange: true,
21997             /**
21998              * @event editorevent
21999              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22000              * @param {HtmlEditor} this
22001              */
22002             editorevent: true,
22003             /**
22004              * @event firstfocus
22005              * Fires when on first focus - needed by toolbars..
22006              * @param {HtmlEditor} this
22007              */
22008             firstfocus: true,
22009             /**
22010              * @event autosave
22011              * Auto save the htmlEditor value as a file into Events
22012              * @param {HtmlEditor} this
22013              */
22014             autosave: true,
22015             /**
22016              * @event savedpreview
22017              * preview the saved version of htmlEditor
22018              * @param {HtmlEditor} this
22019              */
22020             savedpreview: true
22021         });
22022 };
22023
22024
22025 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22026     
22027     
22028       /**
22029      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22030      */
22031     toolbars : false,
22032    
22033      /**
22034      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22035      *                        Roo.resizable.
22036      */
22037     resizable : false,
22038      /**
22039      * @cfg {Number} height (in pixels)
22040      */   
22041     height: 300,
22042    /**
22043      * @cfg {Number} width (in pixels)
22044      */   
22045     width: false,
22046     
22047     /**
22048      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22049      * 
22050      */
22051     stylesheets: false,
22052     
22053     // id of frame..
22054     frameId: false,
22055     
22056     // private properties
22057     validationEvent : false,
22058     deferHeight: true,
22059     initialized : false,
22060     activated : false,
22061     
22062     onFocus : Roo.emptyFn,
22063     iframePad:3,
22064     hideMode:'offsets',
22065     
22066     
22067     tbContainer : false,
22068     
22069     toolbarContainer :function() {
22070         return this.wrap.select('.x-html-editor-tb',true).first();
22071     },
22072
22073     /**
22074      * Protected method that will not generally be called directly. It
22075      * is called when the editor creates its toolbar. Override this method if you need to
22076      * add custom toolbar buttons.
22077      * @param {HtmlEditor} editor
22078      */
22079     createToolbar : function(){
22080         
22081         Roo.log("create toolbars");
22082         
22083         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22084         this.toolbars[0].render(this.toolbarContainer());
22085         
22086         return;
22087         
22088 //        if (!editor.toolbars || !editor.toolbars.length) {
22089 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22090 //        }
22091 //        
22092 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22093 //            editor.toolbars[i] = Roo.factory(
22094 //                    typeof(editor.toolbars[i]) == 'string' ?
22095 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22096 //                Roo.bootstrap.HtmlEditor);
22097 //            editor.toolbars[i].init(editor);
22098 //        }
22099     },
22100
22101      
22102     // private
22103     onRender : function(ct, position)
22104     {
22105        // Roo.log("Call onRender: " + this.xtype);
22106         var _t = this;
22107         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22108       
22109         this.wrap = this.inputEl().wrap({
22110             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22111         });
22112         
22113         this.editorcore.onRender(ct, position);
22114          
22115         if (this.resizable) {
22116             this.resizeEl = new Roo.Resizable(this.wrap, {
22117                 pinned : true,
22118                 wrap: true,
22119                 dynamic : true,
22120                 minHeight : this.height,
22121                 height: this.height,
22122                 handles : this.resizable,
22123                 width: this.width,
22124                 listeners : {
22125                     resize : function(r, w, h) {
22126                         _t.onResize(w,h); // -something
22127                     }
22128                 }
22129             });
22130             
22131         }
22132         this.createToolbar(this);
22133        
22134         
22135         if(!this.width && this.resizable){
22136             this.setSize(this.wrap.getSize());
22137         }
22138         if (this.resizeEl) {
22139             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22140             // should trigger onReize..
22141         }
22142         
22143     },
22144
22145     // private
22146     onResize : function(w, h)
22147     {
22148         Roo.log('resize: ' +w + ',' + h );
22149         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22150         var ew = false;
22151         var eh = false;
22152         
22153         if(this.inputEl() ){
22154             if(typeof w == 'number'){
22155                 var aw = w - this.wrap.getFrameWidth('lr');
22156                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22157                 ew = aw;
22158             }
22159             if(typeof h == 'number'){
22160                  var tbh = -11;  // fixme it needs to tool bar size!
22161                 for (var i =0; i < this.toolbars.length;i++) {
22162                     // fixme - ask toolbars for heights?
22163                     tbh += this.toolbars[i].el.getHeight();
22164                     //if (this.toolbars[i].footer) {
22165                     //    tbh += this.toolbars[i].footer.el.getHeight();
22166                     //}
22167                 }
22168               
22169                 
22170                 
22171                 
22172                 
22173                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22174                 ah -= 5; // knock a few pixes off for look..
22175                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22176                 var eh = ah;
22177             }
22178         }
22179         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22180         this.editorcore.onResize(ew,eh);
22181         
22182     },
22183
22184     /**
22185      * Toggles the editor between standard and source edit mode.
22186      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22187      */
22188     toggleSourceEdit : function(sourceEditMode)
22189     {
22190         this.editorcore.toggleSourceEdit(sourceEditMode);
22191         
22192         if(this.editorcore.sourceEditMode){
22193             Roo.log('editor - showing textarea');
22194             
22195 //            Roo.log('in');
22196 //            Roo.log(this.syncValue());
22197             this.syncValue();
22198             this.inputEl().removeClass(['hide', 'x-hidden']);
22199             this.inputEl().dom.removeAttribute('tabIndex');
22200             this.inputEl().focus();
22201         }else{
22202             Roo.log('editor - hiding textarea');
22203 //            Roo.log('out')
22204 //            Roo.log(this.pushValue()); 
22205             this.pushValue();
22206             
22207             this.inputEl().addClass(['hide', 'x-hidden']);
22208             this.inputEl().dom.setAttribute('tabIndex', -1);
22209             //this.deferFocus();
22210         }
22211          
22212         if(this.resizable){
22213             this.setSize(this.wrap.getSize());
22214         }
22215         
22216         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22217     },
22218  
22219     // private (for BoxComponent)
22220     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22221
22222     // private (for BoxComponent)
22223     getResizeEl : function(){
22224         return this.wrap;
22225     },
22226
22227     // private (for BoxComponent)
22228     getPositionEl : function(){
22229         return this.wrap;
22230     },
22231
22232     // private
22233     initEvents : function(){
22234         this.originalValue = this.getValue();
22235     },
22236
22237 //    /**
22238 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22239 //     * @method
22240 //     */
22241 //    markInvalid : Roo.emptyFn,
22242 //    /**
22243 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22244 //     * @method
22245 //     */
22246 //    clearInvalid : Roo.emptyFn,
22247
22248     setValue : function(v){
22249         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22250         this.editorcore.pushValue();
22251     },
22252
22253      
22254     // private
22255     deferFocus : function(){
22256         this.focus.defer(10, this);
22257     },
22258
22259     // doc'ed in Field
22260     focus : function(){
22261         this.editorcore.focus();
22262         
22263     },
22264       
22265
22266     // private
22267     onDestroy : function(){
22268         
22269         
22270         
22271         if(this.rendered){
22272             
22273             for (var i =0; i < this.toolbars.length;i++) {
22274                 // fixme - ask toolbars for heights?
22275                 this.toolbars[i].onDestroy();
22276             }
22277             
22278             this.wrap.dom.innerHTML = '';
22279             this.wrap.remove();
22280         }
22281     },
22282
22283     // private
22284     onFirstFocus : function(){
22285         //Roo.log("onFirstFocus");
22286         this.editorcore.onFirstFocus();
22287          for (var i =0; i < this.toolbars.length;i++) {
22288             this.toolbars[i].onFirstFocus();
22289         }
22290         
22291     },
22292     
22293     // private
22294     syncValue : function()
22295     {   
22296         this.editorcore.syncValue();
22297     },
22298     
22299     pushValue : function()
22300     {   
22301         this.editorcore.pushValue();
22302     }
22303      
22304     
22305     // hide stuff that is not compatible
22306     /**
22307      * @event blur
22308      * @hide
22309      */
22310     /**
22311      * @event change
22312      * @hide
22313      */
22314     /**
22315      * @event focus
22316      * @hide
22317      */
22318     /**
22319      * @event specialkey
22320      * @hide
22321      */
22322     /**
22323      * @cfg {String} fieldClass @hide
22324      */
22325     /**
22326      * @cfg {String} focusClass @hide
22327      */
22328     /**
22329      * @cfg {String} autoCreate @hide
22330      */
22331     /**
22332      * @cfg {String} inputType @hide
22333      */
22334     /**
22335      * @cfg {String} invalidClass @hide
22336      */
22337     /**
22338      * @cfg {String} invalidText @hide
22339      */
22340     /**
22341      * @cfg {String} msgFx @hide
22342      */
22343     /**
22344      * @cfg {String} validateOnBlur @hide
22345      */
22346 });
22347  
22348     
22349    
22350    
22351    
22352       
22353 Roo.namespace('Roo.bootstrap.htmleditor');
22354 /**
22355  * @class Roo.bootstrap.HtmlEditorToolbar1
22356  * Basic Toolbar
22357  * 
22358  * Usage:
22359  *
22360  new Roo.bootstrap.HtmlEditor({
22361     ....
22362     toolbars : [
22363         new Roo.bootstrap.HtmlEditorToolbar1({
22364             disable : { fonts: 1 , format: 1, ..., ... , ...],
22365             btns : [ .... ]
22366         })
22367     }
22368      
22369  * 
22370  * @cfg {Object} disable List of elements to disable..
22371  * @cfg {Array} btns List of additional buttons.
22372  * 
22373  * 
22374  * NEEDS Extra CSS? 
22375  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22376  */
22377  
22378 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22379 {
22380     
22381     Roo.apply(this, config);
22382     
22383     // default disabled, based on 'good practice'..
22384     this.disable = this.disable || {};
22385     Roo.applyIf(this.disable, {
22386         fontSize : true,
22387         colors : true,
22388         specialElements : true
22389     });
22390     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22391     
22392     this.editor = config.editor;
22393     this.editorcore = config.editor.editorcore;
22394     
22395     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22396     
22397     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22398     // dont call parent... till later.
22399 }
22400 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22401      
22402     bar : true,
22403     
22404     editor : false,
22405     editorcore : false,
22406     
22407     
22408     formats : [
22409         "p" ,  
22410         "h1","h2","h3","h4","h5","h6", 
22411         "pre", "code", 
22412         "abbr", "acronym", "address", "cite", "samp", "var",
22413         'div','span'
22414     ],
22415     
22416     onRender : function(ct, position)
22417     {
22418        // Roo.log("Call onRender: " + this.xtype);
22419         
22420        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22421        Roo.log(this.el);
22422        this.el.dom.style.marginBottom = '0';
22423        var _this = this;
22424        var editorcore = this.editorcore;
22425        var editor= this.editor;
22426        
22427        var children = [];
22428        var btn = function(id,cmd , toggle, handler){
22429        
22430             var  event = toggle ? 'toggle' : 'click';
22431        
22432             var a = {
22433                 size : 'sm',
22434                 xtype: 'Button',
22435                 xns: Roo.bootstrap,
22436                 glyphicon : id,
22437                 cmd : id || cmd,
22438                 enableToggle:toggle !== false,
22439                 //html : 'submit'
22440                 pressed : toggle ? false : null,
22441                 listeners : {}
22442             };
22443             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22444                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22445             };
22446             children.push(a);
22447             return a;
22448        }
22449         
22450         var style = {
22451                 xtype: 'Button',
22452                 size : 'sm',
22453                 xns: Roo.bootstrap,
22454                 glyphicon : 'font',
22455                 //html : 'submit'
22456                 menu : {
22457                     xtype: 'Menu',
22458                     xns: Roo.bootstrap,
22459                     items:  []
22460                 }
22461         };
22462         Roo.each(this.formats, function(f) {
22463             style.menu.items.push({
22464                 xtype :'MenuItem',
22465                 xns: Roo.bootstrap,
22466                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22467                 tagname : f,
22468                 listeners : {
22469                     click : function()
22470                     {
22471                         editorcore.insertTag(this.tagname);
22472                         editor.focus();
22473                     }
22474                 }
22475                 
22476             });
22477         });
22478          children.push(style);   
22479             
22480             
22481         btn('bold',false,true);
22482         btn('italic',false,true);
22483         btn('align-left', 'justifyleft',true);
22484         btn('align-center', 'justifycenter',true);
22485         btn('align-right' , 'justifyright',true);
22486         btn('link', false, false, function(btn) {
22487             //Roo.log("create link?");
22488             var url = prompt(this.createLinkText, this.defaultLinkValue);
22489             if(url && url != 'http:/'+'/'){
22490                 this.editorcore.relayCmd('createlink', url);
22491             }
22492         }),
22493         btn('list','insertunorderedlist',true);
22494         btn('pencil', false,true, function(btn){
22495                 Roo.log(this);
22496                 
22497                 this.toggleSourceEdit(btn.pressed);
22498         });
22499         /*
22500         var cog = {
22501                 xtype: 'Button',
22502                 size : 'sm',
22503                 xns: Roo.bootstrap,
22504                 glyphicon : 'cog',
22505                 //html : 'submit'
22506                 menu : {
22507                     xtype: 'Menu',
22508                     xns: Roo.bootstrap,
22509                     items:  []
22510                 }
22511         };
22512         
22513         cog.menu.items.push({
22514             xtype :'MenuItem',
22515             xns: Roo.bootstrap,
22516             html : Clean styles,
22517             tagname : f,
22518             listeners : {
22519                 click : function()
22520                 {
22521                     editorcore.insertTag(this.tagname);
22522                     editor.focus();
22523                 }
22524             }
22525             
22526         });
22527        */
22528         
22529          
22530        this.xtype = 'NavSimplebar';
22531         
22532         for(var i=0;i< children.length;i++) {
22533             
22534             this.buttons.add(this.addxtypeChild(children[i]));
22535             
22536         }
22537         
22538         editor.on('editorevent', this.updateToolbar, this);
22539     },
22540     onBtnClick : function(id)
22541     {
22542        this.editorcore.relayCmd(id);
22543        this.editorcore.focus();
22544     },
22545     
22546     /**
22547      * Protected method that will not generally be called directly. It triggers
22548      * a toolbar update by reading the markup state of the current selection in the editor.
22549      */
22550     updateToolbar: function(){
22551
22552         if(!this.editorcore.activated){
22553             this.editor.onFirstFocus(); // is this neeed?
22554             return;
22555         }
22556
22557         var btns = this.buttons; 
22558         var doc = this.editorcore.doc;
22559         btns.get('bold').setActive(doc.queryCommandState('bold'));
22560         btns.get('italic').setActive(doc.queryCommandState('italic'));
22561         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22562         
22563         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22564         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22565         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22566         
22567         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22568         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22569          /*
22570         
22571         var ans = this.editorcore.getAllAncestors();
22572         if (this.formatCombo) {
22573             
22574             
22575             var store = this.formatCombo.store;
22576             this.formatCombo.setValue("");
22577             for (var i =0; i < ans.length;i++) {
22578                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22579                     // select it..
22580                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22581                     break;
22582                 }
22583             }
22584         }
22585         
22586         
22587         
22588         // hides menus... - so this cant be on a menu...
22589         Roo.bootstrap.MenuMgr.hideAll();
22590         */
22591         Roo.bootstrap.MenuMgr.hideAll();
22592         //this.editorsyncValue();
22593     },
22594     onFirstFocus: function() {
22595         this.buttons.each(function(item){
22596            item.enable();
22597         });
22598     },
22599     toggleSourceEdit : function(sourceEditMode){
22600         
22601           
22602         if(sourceEditMode){
22603             Roo.log("disabling buttons");
22604            this.buttons.each( function(item){
22605                 if(item.cmd != 'pencil'){
22606                     item.disable();
22607                 }
22608             });
22609           
22610         }else{
22611             Roo.log("enabling buttons");
22612             if(this.editorcore.initialized){
22613                 this.buttons.each( function(item){
22614                     item.enable();
22615                 });
22616             }
22617             
22618         }
22619         Roo.log("calling toggole on editor");
22620         // tell the editor that it's been pressed..
22621         this.editor.toggleSourceEdit(sourceEditMode);
22622        
22623     }
22624 });
22625
22626
22627
22628
22629
22630 /**
22631  * @class Roo.bootstrap.Table.AbstractSelectionModel
22632  * @extends Roo.util.Observable
22633  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22634  * implemented by descendant classes.  This class should not be directly instantiated.
22635  * @constructor
22636  */
22637 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22638     this.locked = false;
22639     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22640 };
22641
22642
22643 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22644     /** @ignore Called by the grid automatically. Do not call directly. */
22645     init : function(grid){
22646         this.grid = grid;
22647         this.initEvents();
22648     },
22649
22650     /**
22651      * Locks the selections.
22652      */
22653     lock : function(){
22654         this.locked = true;
22655     },
22656
22657     /**
22658      * Unlocks the selections.
22659      */
22660     unlock : function(){
22661         this.locked = false;
22662     },
22663
22664     /**
22665      * Returns true if the selections are locked.
22666      * @return {Boolean}
22667      */
22668     isLocked : function(){
22669         return this.locked;
22670     }
22671 });
22672 /**
22673  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22674  * @class Roo.bootstrap.Table.RowSelectionModel
22675  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22676  * It supports multiple selections and keyboard selection/navigation. 
22677  * @constructor
22678  * @param {Object} config
22679  */
22680
22681 Roo.bootstrap.Table.RowSelectionModel = function(config){
22682     Roo.apply(this, config);
22683     this.selections = new Roo.util.MixedCollection(false, function(o){
22684         return o.id;
22685     });
22686
22687     this.last = false;
22688     this.lastActive = false;
22689
22690     this.addEvents({
22691         /**
22692              * @event selectionchange
22693              * Fires when the selection changes
22694              * @param {SelectionModel} this
22695              */
22696             "selectionchange" : true,
22697         /**
22698              * @event afterselectionchange
22699              * Fires after the selection changes (eg. by key press or clicking)
22700              * @param {SelectionModel} this
22701              */
22702             "afterselectionchange" : true,
22703         /**
22704              * @event beforerowselect
22705              * Fires when a row is selected being selected, return false to cancel.
22706              * @param {SelectionModel} this
22707              * @param {Number} rowIndex The selected index
22708              * @param {Boolean} keepExisting False if other selections will be cleared
22709              */
22710             "beforerowselect" : true,
22711         /**
22712              * @event rowselect
22713              * Fires when a row is selected.
22714              * @param {SelectionModel} this
22715              * @param {Number} rowIndex The selected index
22716              * @param {Roo.data.Record} r The record
22717              */
22718             "rowselect" : true,
22719         /**
22720              * @event rowdeselect
22721              * Fires when a row is deselected.
22722              * @param {SelectionModel} this
22723              * @param {Number} rowIndex The selected index
22724              */
22725         "rowdeselect" : true
22726     });
22727     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22728     this.locked = false;
22729  };
22730
22731 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22732     /**
22733      * @cfg {Boolean} singleSelect
22734      * True to allow selection of only one row at a time (defaults to false)
22735      */
22736     singleSelect : false,
22737
22738     // private
22739     initEvents : function()
22740     {
22741
22742         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22743         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22744         //}else{ // allow click to work like normal
22745          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22746         //}
22747         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22748         this.grid.on("rowclick", this.handleMouseDown, this);
22749         
22750         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22751             "up" : function(e){
22752                 if(!e.shiftKey){
22753                     this.selectPrevious(e.shiftKey);
22754                 }else if(this.last !== false && this.lastActive !== false){
22755                     var last = this.last;
22756                     this.selectRange(this.last,  this.lastActive-1);
22757                     this.grid.getView().focusRow(this.lastActive);
22758                     if(last !== false){
22759                         this.last = last;
22760                     }
22761                 }else{
22762                     this.selectFirstRow();
22763                 }
22764                 this.fireEvent("afterselectionchange", this);
22765             },
22766             "down" : function(e){
22767                 if(!e.shiftKey){
22768                     this.selectNext(e.shiftKey);
22769                 }else if(this.last !== false && this.lastActive !== false){
22770                     var last = this.last;
22771                     this.selectRange(this.last,  this.lastActive+1);
22772                     this.grid.getView().focusRow(this.lastActive);
22773                     if(last !== false){
22774                         this.last = last;
22775                     }
22776                 }else{
22777                     this.selectFirstRow();
22778                 }
22779                 this.fireEvent("afterselectionchange", this);
22780             },
22781             scope: this
22782         });
22783         this.grid.store.on('load', function(){
22784             this.selections.clear();
22785         },this);
22786         /*
22787         var view = this.grid.view;
22788         view.on("refresh", this.onRefresh, this);
22789         view.on("rowupdated", this.onRowUpdated, this);
22790         view.on("rowremoved", this.onRemove, this);
22791         */
22792     },
22793
22794     // private
22795     onRefresh : function()
22796     {
22797         var ds = this.grid.store, i, v = this.grid.view;
22798         var s = this.selections;
22799         s.each(function(r){
22800             if((i = ds.indexOfId(r.id)) != -1){
22801                 v.onRowSelect(i);
22802             }else{
22803                 s.remove(r);
22804             }
22805         });
22806     },
22807
22808     // private
22809     onRemove : function(v, index, r){
22810         this.selections.remove(r);
22811     },
22812
22813     // private
22814     onRowUpdated : function(v, index, r){
22815         if(this.isSelected(r)){
22816             v.onRowSelect(index);
22817         }
22818     },
22819
22820     /**
22821      * Select records.
22822      * @param {Array} records The records to select
22823      * @param {Boolean} keepExisting (optional) True to keep existing selections
22824      */
22825     selectRecords : function(records, keepExisting)
22826     {
22827         if(!keepExisting){
22828             this.clearSelections();
22829         }
22830             var ds = this.grid.store;
22831         for(var i = 0, len = records.length; i < len; i++){
22832             this.selectRow(ds.indexOf(records[i]), true);
22833         }
22834     },
22835
22836     /**
22837      * Gets the number of selected rows.
22838      * @return {Number}
22839      */
22840     getCount : function(){
22841         return this.selections.length;
22842     },
22843
22844     /**
22845      * Selects the first row in the grid.
22846      */
22847     selectFirstRow : function(){
22848         this.selectRow(0);
22849     },
22850
22851     /**
22852      * Select the last row.
22853      * @param {Boolean} keepExisting (optional) True to keep existing selections
22854      */
22855     selectLastRow : function(keepExisting){
22856         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22857         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22858     },
22859
22860     /**
22861      * Selects the row immediately following the last selected row.
22862      * @param {Boolean} keepExisting (optional) True to keep existing selections
22863      */
22864     selectNext : function(keepExisting)
22865     {
22866             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22867             this.selectRow(this.last+1, keepExisting);
22868             this.grid.getView().focusRow(this.last);
22869         }
22870     },
22871
22872     /**
22873      * Selects the row that precedes the last selected row.
22874      * @param {Boolean} keepExisting (optional) True to keep existing selections
22875      */
22876     selectPrevious : function(keepExisting){
22877         if(this.last){
22878             this.selectRow(this.last-1, keepExisting);
22879             this.grid.getView().focusRow(this.last);
22880         }
22881     },
22882
22883     /**
22884      * Returns the selected records
22885      * @return {Array} Array of selected records
22886      */
22887     getSelections : function(){
22888         return [].concat(this.selections.items);
22889     },
22890
22891     /**
22892      * Returns the first selected record.
22893      * @return {Record}
22894      */
22895     getSelected : function(){
22896         return this.selections.itemAt(0);
22897     },
22898
22899
22900     /**
22901      * Clears all selections.
22902      */
22903     clearSelections : function(fast)
22904     {
22905         if(this.locked) {
22906             return;
22907         }
22908         if(fast !== true){
22909                 var ds = this.grid.store;
22910             var s = this.selections;
22911             s.each(function(r){
22912                 this.deselectRow(ds.indexOfId(r.id));
22913             }, this);
22914             s.clear();
22915         }else{
22916             this.selections.clear();
22917         }
22918         this.last = false;
22919     },
22920
22921
22922     /**
22923      * Selects all rows.
22924      */
22925     selectAll : function(){
22926         if(this.locked) {
22927             return;
22928         }
22929         this.selections.clear();
22930         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22931             this.selectRow(i, true);
22932         }
22933     },
22934
22935     /**
22936      * Returns True if there is a selection.
22937      * @return {Boolean}
22938      */
22939     hasSelection : function(){
22940         return this.selections.length > 0;
22941     },
22942
22943     /**
22944      * Returns True if the specified row is selected.
22945      * @param {Number/Record} record The record or index of the record to check
22946      * @return {Boolean}
22947      */
22948     isSelected : function(index){
22949             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22950         return (r && this.selections.key(r.id) ? true : false);
22951     },
22952
22953     /**
22954      * Returns True if the specified record id is selected.
22955      * @param {String} id The id of record to check
22956      * @return {Boolean}
22957      */
22958     isIdSelected : function(id){
22959         return (this.selections.key(id) ? true : false);
22960     },
22961
22962
22963     // private
22964     handleMouseDBClick : function(e, t){
22965         
22966     },
22967     // private
22968     handleMouseDown : function(e, t)
22969     {
22970             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22971         if(this.isLocked() || rowIndex < 0 ){
22972             return;
22973         };
22974         if(e.shiftKey && this.last !== false){
22975             var last = this.last;
22976             this.selectRange(last, rowIndex, e.ctrlKey);
22977             this.last = last; // reset the last
22978             t.focus();
22979     
22980         }else{
22981             var isSelected = this.isSelected(rowIndex);
22982             //Roo.log("select row:" + rowIndex);
22983             if(isSelected){
22984                 this.deselectRow(rowIndex);
22985             } else {
22986                         this.selectRow(rowIndex, true);
22987             }
22988     
22989             /*
22990                 if(e.button !== 0 && isSelected){
22991                 alert('rowIndex 2: ' + rowIndex);
22992                     view.focusRow(rowIndex);
22993                 }else if(e.ctrlKey && isSelected){
22994                     this.deselectRow(rowIndex);
22995                 }else if(!isSelected){
22996                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22997                     view.focusRow(rowIndex);
22998                 }
22999             */
23000         }
23001         this.fireEvent("afterselectionchange", this);
23002     },
23003     // private
23004     handleDragableRowClick :  function(grid, rowIndex, e) 
23005     {
23006         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23007             this.selectRow(rowIndex, false);
23008             grid.view.focusRow(rowIndex);
23009              this.fireEvent("afterselectionchange", this);
23010         }
23011     },
23012     
23013     /**
23014      * Selects multiple rows.
23015      * @param {Array} rows Array of the indexes of the row to select
23016      * @param {Boolean} keepExisting (optional) True to keep existing selections
23017      */
23018     selectRows : function(rows, keepExisting){
23019         if(!keepExisting){
23020             this.clearSelections();
23021         }
23022         for(var i = 0, len = rows.length; i < len; i++){
23023             this.selectRow(rows[i], true);
23024         }
23025     },
23026
23027     /**
23028      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23029      * @param {Number} startRow The index of the first row in the range
23030      * @param {Number} endRow The index of the last row in the range
23031      * @param {Boolean} keepExisting (optional) True to retain existing selections
23032      */
23033     selectRange : function(startRow, endRow, keepExisting){
23034         if(this.locked) {
23035             return;
23036         }
23037         if(!keepExisting){
23038             this.clearSelections();
23039         }
23040         if(startRow <= endRow){
23041             for(var i = startRow; i <= endRow; i++){
23042                 this.selectRow(i, true);
23043             }
23044         }else{
23045             for(var i = startRow; i >= endRow; i--){
23046                 this.selectRow(i, true);
23047             }
23048         }
23049     },
23050
23051     /**
23052      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23053      * @param {Number} startRow The index of the first row in the range
23054      * @param {Number} endRow The index of the last row in the range
23055      */
23056     deselectRange : function(startRow, endRow, preventViewNotify){
23057         if(this.locked) {
23058             return;
23059         }
23060         for(var i = startRow; i <= endRow; i++){
23061             this.deselectRow(i, preventViewNotify);
23062         }
23063     },
23064
23065     /**
23066      * Selects a row.
23067      * @param {Number} row The index of the row to select
23068      * @param {Boolean} keepExisting (optional) True to keep existing selections
23069      */
23070     selectRow : function(index, keepExisting, preventViewNotify)
23071     {
23072             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23073             return;
23074         }
23075         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23076             if(!keepExisting || this.singleSelect){
23077                 this.clearSelections();
23078             }
23079             
23080             var r = this.grid.store.getAt(index);
23081             //console.log('selectRow - record id :' + r.id);
23082             
23083             this.selections.add(r);
23084             this.last = this.lastActive = index;
23085             if(!preventViewNotify){
23086                 var proxy = new Roo.Element(
23087                                 this.grid.getRowDom(index)
23088                 );
23089                 proxy.addClass('bg-info info');
23090             }
23091             this.fireEvent("rowselect", this, index, r);
23092             this.fireEvent("selectionchange", this);
23093         }
23094     },
23095
23096     /**
23097      * Deselects a row.
23098      * @param {Number} row The index of the row to deselect
23099      */
23100     deselectRow : function(index, preventViewNotify)
23101     {
23102         if(this.locked) {
23103             return;
23104         }
23105         if(this.last == index){
23106             this.last = false;
23107         }
23108         if(this.lastActive == index){
23109             this.lastActive = false;
23110         }
23111         
23112         var r = this.grid.store.getAt(index);
23113         if (!r) {
23114             return;
23115         }
23116         
23117         this.selections.remove(r);
23118         //.console.log('deselectRow - record id :' + r.id);
23119         if(!preventViewNotify){
23120         
23121             var proxy = new Roo.Element(
23122                 this.grid.getRowDom(index)
23123             );
23124             proxy.removeClass('bg-info info');
23125         }
23126         this.fireEvent("rowdeselect", this, index);
23127         this.fireEvent("selectionchange", this);
23128     },
23129
23130     // private
23131     restoreLast : function(){
23132         if(this._last){
23133             this.last = this._last;
23134         }
23135     },
23136
23137     // private
23138     acceptsNav : function(row, col, cm){
23139         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23140     },
23141
23142     // private
23143     onEditorKey : function(field, e){
23144         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23145         if(k == e.TAB){
23146             e.stopEvent();
23147             ed.completeEdit();
23148             if(e.shiftKey){
23149                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23150             }else{
23151                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23152             }
23153         }else if(k == e.ENTER && !e.ctrlKey){
23154             e.stopEvent();
23155             ed.completeEdit();
23156             if(e.shiftKey){
23157                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23158             }else{
23159                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23160             }
23161         }else if(k == e.ESC){
23162             ed.cancelEdit();
23163         }
23164         if(newCell){
23165             g.startEditing(newCell[0], newCell[1]);
23166         }
23167     }
23168 });
23169 /*
23170  * Based on:
23171  * Ext JS Library 1.1.1
23172  * Copyright(c) 2006-2007, Ext JS, LLC.
23173  *
23174  * Originally Released Under LGPL - original licence link has changed is not relivant.
23175  *
23176  * Fork - LGPL
23177  * <script type="text/javascript">
23178  */
23179  
23180 /**
23181  * @class Roo.bootstrap.PagingToolbar
23182  * @extends Roo.bootstrap.NavSimplebar
23183  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23184  * @constructor
23185  * Create a new PagingToolbar
23186  * @param {Object} config The config object
23187  * @param {Roo.data.Store} store
23188  */
23189 Roo.bootstrap.PagingToolbar = function(config)
23190 {
23191     // old args format still supported... - xtype is prefered..
23192         // created from xtype...
23193     
23194     this.ds = config.dataSource;
23195     
23196     if (config.store && !this.ds) {
23197         this.store= Roo.factory(config.store, Roo.data);
23198         this.ds = this.store;
23199         this.ds.xmodule = this.xmodule || false;
23200     }
23201     
23202     this.toolbarItems = [];
23203     if (config.items) {
23204         this.toolbarItems = config.items;
23205     }
23206     
23207     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23208     
23209     this.cursor = 0;
23210     
23211     if (this.ds) { 
23212         this.bind(this.ds);
23213     }
23214     
23215     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23216     
23217 };
23218
23219 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23220     /**
23221      * @cfg {Roo.data.Store} dataSource
23222      * The underlying data store providing the paged data
23223      */
23224     /**
23225      * @cfg {String/HTMLElement/Element} container
23226      * container The id or element that will contain the toolbar
23227      */
23228     /**
23229      * @cfg {Boolean} displayInfo
23230      * True to display the displayMsg (defaults to false)
23231      */
23232     /**
23233      * @cfg {Number} pageSize
23234      * The number of records to display per page (defaults to 20)
23235      */
23236     pageSize: 20,
23237     /**
23238      * @cfg {String} displayMsg
23239      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23240      */
23241     displayMsg : 'Displaying {0} - {1} of {2}',
23242     /**
23243      * @cfg {String} emptyMsg
23244      * The message to display when no records are found (defaults to "No data to display")
23245      */
23246     emptyMsg : 'No data to display',
23247     /**
23248      * Customizable piece of the default paging text (defaults to "Page")
23249      * @type String
23250      */
23251     beforePageText : "Page",
23252     /**
23253      * Customizable piece of the default paging text (defaults to "of %0")
23254      * @type String
23255      */
23256     afterPageText : "of {0}",
23257     /**
23258      * Customizable piece of the default paging text (defaults to "First Page")
23259      * @type String
23260      */
23261     firstText : "First Page",
23262     /**
23263      * Customizable piece of the default paging text (defaults to "Previous Page")
23264      * @type String
23265      */
23266     prevText : "Previous Page",
23267     /**
23268      * Customizable piece of the default paging text (defaults to "Next Page")
23269      * @type String
23270      */
23271     nextText : "Next Page",
23272     /**
23273      * Customizable piece of the default paging text (defaults to "Last Page")
23274      * @type String
23275      */
23276     lastText : "Last Page",
23277     /**
23278      * Customizable piece of the default paging text (defaults to "Refresh")
23279      * @type String
23280      */
23281     refreshText : "Refresh",
23282
23283     buttons : false,
23284     // private
23285     onRender : function(ct, position) 
23286     {
23287         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23288         this.navgroup.parentId = this.id;
23289         this.navgroup.onRender(this.el, null);
23290         // add the buttons to the navgroup
23291         
23292         if(this.displayInfo){
23293             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23294             this.displayEl = this.el.select('.x-paging-info', true).first();
23295 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23296 //            this.displayEl = navel.el.select('span',true).first();
23297         }
23298         
23299         var _this = this;
23300         
23301         if(this.buttons){
23302             Roo.each(_this.buttons, function(e){ // this might need to use render????
23303                Roo.factory(e).onRender(_this.el, null);
23304             });
23305         }
23306             
23307         Roo.each(_this.toolbarItems, function(e) {
23308             _this.navgroup.addItem(e);
23309         });
23310         
23311         
23312         this.first = this.navgroup.addItem({
23313             tooltip: this.firstText,
23314             cls: "prev",
23315             icon : 'fa fa-backward',
23316             disabled: true,
23317             preventDefault: true,
23318             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23319         });
23320         
23321         this.prev =  this.navgroup.addItem({
23322             tooltip: this.prevText,
23323             cls: "prev",
23324             icon : 'fa fa-step-backward',
23325             disabled: true,
23326             preventDefault: true,
23327             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23328         });
23329     //this.addSeparator();
23330         
23331         
23332         var field = this.navgroup.addItem( {
23333             tagtype : 'span',
23334             cls : 'x-paging-position',
23335             
23336             html : this.beforePageText  +
23337                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23338                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23339          } ); //?? escaped?
23340         
23341         this.field = field.el.select('input', true).first();
23342         this.field.on("keydown", this.onPagingKeydown, this);
23343         this.field.on("focus", function(){this.dom.select();});
23344     
23345     
23346         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23347         //this.field.setHeight(18);
23348         //this.addSeparator();
23349         this.next = this.navgroup.addItem({
23350             tooltip: this.nextText,
23351             cls: "next",
23352             html : ' <i class="fa fa-step-forward">',
23353             disabled: true,
23354             preventDefault: true,
23355             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23356         });
23357         this.last = this.navgroup.addItem({
23358             tooltip: this.lastText,
23359             icon : 'fa fa-forward',
23360             cls: "next",
23361             disabled: true,
23362             preventDefault: true,
23363             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23364         });
23365     //this.addSeparator();
23366         this.loading = this.navgroup.addItem({
23367             tooltip: this.refreshText,
23368             icon: 'fa fa-refresh',
23369             preventDefault: true,
23370             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23371         });
23372         
23373     },
23374
23375     // private
23376     updateInfo : function(){
23377         if(this.displayEl){
23378             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23379             var msg = count == 0 ?
23380                 this.emptyMsg :
23381                 String.format(
23382                     this.displayMsg,
23383                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23384                 );
23385             this.displayEl.update(msg);
23386         }
23387     },
23388
23389     // private
23390     onLoad : function(ds, r, o){
23391        this.cursor = o.params ? o.params.start : 0;
23392        var d = this.getPageData(),
23393             ap = d.activePage,
23394             ps = d.pages;
23395         
23396        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23397        this.field.dom.value = ap;
23398        this.first.setDisabled(ap == 1);
23399        this.prev.setDisabled(ap == 1);
23400        this.next.setDisabled(ap == ps);
23401        this.last.setDisabled(ap == ps);
23402        this.loading.enable();
23403        this.updateInfo();
23404     },
23405
23406     // private
23407     getPageData : function(){
23408         var total = this.ds.getTotalCount();
23409         return {
23410             total : total,
23411             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23412             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23413         };
23414     },
23415
23416     // private
23417     onLoadError : function(){
23418         this.loading.enable();
23419     },
23420
23421     // private
23422     onPagingKeydown : function(e){
23423         var k = e.getKey();
23424         var d = this.getPageData();
23425         if(k == e.RETURN){
23426             var v = this.field.dom.value, pageNum;
23427             if(!v || isNaN(pageNum = parseInt(v, 10))){
23428                 this.field.dom.value = d.activePage;
23429                 return;
23430             }
23431             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23432             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23433             e.stopEvent();
23434         }
23435         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))
23436         {
23437           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23438           this.field.dom.value = pageNum;
23439           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23440           e.stopEvent();
23441         }
23442         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23443         {
23444           var v = this.field.dom.value, pageNum; 
23445           var increment = (e.shiftKey) ? 10 : 1;
23446           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23447                 increment *= -1;
23448           }
23449           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23450             this.field.dom.value = d.activePage;
23451             return;
23452           }
23453           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23454           {
23455             this.field.dom.value = parseInt(v, 10) + increment;
23456             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23457             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23458           }
23459           e.stopEvent();
23460         }
23461     },
23462
23463     // private
23464     beforeLoad : function(){
23465         if(this.loading){
23466             this.loading.disable();
23467         }
23468     },
23469
23470     // private
23471     onClick : function(which){
23472         
23473         var ds = this.ds;
23474         if (!ds) {
23475             return;
23476         }
23477         
23478         switch(which){
23479             case "first":
23480                 ds.load({params:{start: 0, limit: this.pageSize}});
23481             break;
23482             case "prev":
23483                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23484             break;
23485             case "next":
23486                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23487             break;
23488             case "last":
23489                 var total = ds.getTotalCount();
23490                 var extra = total % this.pageSize;
23491                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23492                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23493             break;
23494             case "refresh":
23495                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23496             break;
23497         }
23498     },
23499
23500     /**
23501      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23502      * @param {Roo.data.Store} store The data store to unbind
23503      */
23504     unbind : function(ds){
23505         ds.un("beforeload", this.beforeLoad, this);
23506         ds.un("load", this.onLoad, this);
23507         ds.un("loadexception", this.onLoadError, this);
23508         ds.un("remove", this.updateInfo, this);
23509         ds.un("add", this.updateInfo, this);
23510         this.ds = undefined;
23511     },
23512
23513     /**
23514      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23515      * @param {Roo.data.Store} store The data store to bind
23516      */
23517     bind : function(ds){
23518         ds.on("beforeload", this.beforeLoad, this);
23519         ds.on("load", this.onLoad, this);
23520         ds.on("loadexception", this.onLoadError, this);
23521         ds.on("remove", this.updateInfo, this);
23522         ds.on("add", this.updateInfo, this);
23523         this.ds = ds;
23524     }
23525 });/*
23526  * - LGPL
23527  *
23528  * element
23529  * 
23530  */
23531
23532 /**
23533  * @class Roo.bootstrap.MessageBar
23534  * @extends Roo.bootstrap.Component
23535  * Bootstrap MessageBar class
23536  * @cfg {String} html contents of the MessageBar
23537  * @cfg {String} weight (info | success | warning | danger) default info
23538  * @cfg {String} beforeClass insert the bar before the given class
23539  * @cfg {Boolean} closable (true | false) default false
23540  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23541  * 
23542  * @constructor
23543  * Create a new Element
23544  * @param {Object} config The config object
23545  */
23546
23547 Roo.bootstrap.MessageBar = function(config){
23548     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23549 };
23550
23551 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23552     
23553     html: '',
23554     weight: 'info',
23555     closable: false,
23556     fixed: false,
23557     beforeClass: 'bootstrap-sticky-wrap',
23558     
23559     getAutoCreate : function(){
23560         
23561         var cfg = {
23562             tag: 'div',
23563             cls: 'alert alert-dismissable alert-' + this.weight,
23564             cn: [
23565                 {
23566                     tag: 'span',
23567                     cls: 'message',
23568                     html: this.html || ''
23569                 }
23570             ]
23571         };
23572         
23573         if(this.fixed){
23574             cfg.cls += ' alert-messages-fixed';
23575         }
23576         
23577         if(this.closable){
23578             cfg.cn.push({
23579                 tag: 'button',
23580                 cls: 'close',
23581                 html: 'x'
23582             });
23583         }
23584         
23585         return cfg;
23586     },
23587     
23588     onRender : function(ct, position)
23589     {
23590         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23591         
23592         if(!this.el){
23593             var cfg = Roo.apply({},  this.getAutoCreate());
23594             cfg.id = Roo.id();
23595             
23596             if (this.cls) {
23597                 cfg.cls += ' ' + this.cls;
23598             }
23599             if (this.style) {
23600                 cfg.style = this.style;
23601             }
23602             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23603             
23604             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23605         }
23606         
23607         this.el.select('>button.close').on('click', this.hide, this);
23608         
23609     },
23610     
23611     show : function()
23612     {
23613         if (!this.rendered) {
23614             this.render();
23615         }
23616         
23617         this.el.show();
23618         
23619         this.fireEvent('show', this);
23620         
23621     },
23622     
23623     hide : function()
23624     {
23625         if (!this.rendered) {
23626             this.render();
23627         }
23628         
23629         this.el.hide();
23630         
23631         this.fireEvent('hide', this);
23632     },
23633     
23634     update : function()
23635     {
23636 //        var e = this.el.dom.firstChild;
23637 //        
23638 //        if(this.closable){
23639 //            e = e.nextSibling;
23640 //        }
23641 //        
23642 //        e.data = this.html || '';
23643
23644         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23645     }
23646    
23647 });
23648
23649  
23650
23651      /*
23652  * - LGPL
23653  *
23654  * Graph
23655  * 
23656  */
23657
23658
23659 /**
23660  * @class Roo.bootstrap.Graph
23661  * @extends Roo.bootstrap.Component
23662  * Bootstrap Graph class
23663 > Prameters
23664  -sm {number} sm 4
23665  -md {number} md 5
23666  @cfg {String} graphtype  bar | vbar | pie
23667  @cfg {number} g_x coodinator | centre x (pie)
23668  @cfg {number} g_y coodinator | centre y (pie)
23669  @cfg {number} g_r radius (pie)
23670  @cfg {number} g_height height of the chart (respected by all elements in the set)
23671  @cfg {number} g_width width of the chart (respected by all elements in the set)
23672  @cfg {Object} title The title of the chart
23673     
23674  -{Array}  values
23675  -opts (object) options for the chart 
23676      o {
23677      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23678      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23679      o vgutter (number)
23680      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.
23681      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23682      o to
23683      o stretch (boolean)
23684      o }
23685  -opts (object) options for the pie
23686      o{
23687      o cut
23688      o startAngle (number)
23689      o endAngle (number)
23690      } 
23691  *
23692  * @constructor
23693  * Create a new Input
23694  * @param {Object} config The config object
23695  */
23696
23697 Roo.bootstrap.Graph = function(config){
23698     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23699     
23700     this.addEvents({
23701         // img events
23702         /**
23703          * @event click
23704          * The img click event for the img.
23705          * @param {Roo.EventObject} e
23706          */
23707         "click" : true
23708     });
23709 };
23710
23711 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23712     
23713     sm: 4,
23714     md: 5,
23715     graphtype: 'bar',
23716     g_height: 250,
23717     g_width: 400,
23718     g_x: 50,
23719     g_y: 50,
23720     g_r: 30,
23721     opts:{
23722         //g_colors: this.colors,
23723         g_type: 'soft',
23724         g_gutter: '20%'
23725
23726     },
23727     title : false,
23728
23729     getAutoCreate : function(){
23730         
23731         var cfg = {
23732             tag: 'div',
23733             html : null
23734         };
23735         
23736         
23737         return  cfg;
23738     },
23739
23740     onRender : function(ct,position){
23741         
23742         
23743         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23744         
23745         if (typeof(Raphael) == 'undefined') {
23746             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23747             return;
23748         }
23749         
23750         this.raphael = Raphael(this.el.dom);
23751         
23752                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23753                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23754                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23755                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23756                 /*
23757                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23758                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23759                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23760                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23761                 
23762                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23763                 r.barchart(330, 10, 300, 220, data1);
23764                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23765                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23766                 */
23767                 
23768                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23769                 // r.barchart(30, 30, 560, 250,  xdata, {
23770                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23771                 //     axis : "0 0 1 1",
23772                 //     axisxlabels :  xdata
23773                 //     //yvalues : cols,
23774                    
23775                 // });
23776 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23777 //        
23778 //        this.load(null,xdata,{
23779 //                axis : "0 0 1 1",
23780 //                axisxlabels :  xdata
23781 //                });
23782
23783     },
23784
23785     load : function(graphtype,xdata,opts)
23786     {
23787         this.raphael.clear();
23788         if(!graphtype) {
23789             graphtype = this.graphtype;
23790         }
23791         if(!opts){
23792             opts = this.opts;
23793         }
23794         var r = this.raphael,
23795             fin = function () {
23796                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23797             },
23798             fout = function () {
23799                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23800             },
23801             pfin = function() {
23802                 this.sector.stop();
23803                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23804
23805                 if (this.label) {
23806                     this.label[0].stop();
23807                     this.label[0].attr({ r: 7.5 });
23808                     this.label[1].attr({ "font-weight": 800 });
23809                 }
23810             },
23811             pfout = function() {
23812                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23813
23814                 if (this.label) {
23815                     this.label[0].animate({ r: 5 }, 500, "bounce");
23816                     this.label[1].attr({ "font-weight": 400 });
23817                 }
23818             };
23819
23820         switch(graphtype){
23821             case 'bar':
23822                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23823                 break;
23824             case 'hbar':
23825                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23826                 break;
23827             case 'pie':
23828 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23829 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23830 //            
23831                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23832                 
23833                 break;
23834
23835         }
23836         
23837         if(this.title){
23838             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23839         }
23840         
23841     },
23842     
23843     setTitle: function(o)
23844     {
23845         this.title = o;
23846     },
23847     
23848     initEvents: function() {
23849         
23850         if(!this.href){
23851             this.el.on('click', this.onClick, this);
23852         }
23853     },
23854     
23855     onClick : function(e)
23856     {
23857         Roo.log('img onclick');
23858         this.fireEvent('click', this, e);
23859     }
23860    
23861 });
23862
23863  
23864 /*
23865  * - LGPL
23866  *
23867  * numberBox
23868  * 
23869  */
23870 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23871
23872 /**
23873  * @class Roo.bootstrap.dash.NumberBox
23874  * @extends Roo.bootstrap.Component
23875  * Bootstrap NumberBox class
23876  * @cfg {String} headline Box headline
23877  * @cfg {String} content Box content
23878  * @cfg {String} icon Box icon
23879  * @cfg {String} footer Footer text
23880  * @cfg {String} fhref Footer href
23881  * 
23882  * @constructor
23883  * Create a new NumberBox
23884  * @param {Object} config The config object
23885  */
23886
23887
23888 Roo.bootstrap.dash.NumberBox = function(config){
23889     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23890     
23891 };
23892
23893 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23894     
23895     headline : '',
23896     content : '',
23897     icon : '',
23898     footer : '',
23899     fhref : '',
23900     ficon : '',
23901     
23902     getAutoCreate : function(){
23903         
23904         var cfg = {
23905             tag : 'div',
23906             cls : 'small-box ',
23907             cn : [
23908                 {
23909                     tag : 'div',
23910                     cls : 'inner',
23911                     cn :[
23912                         {
23913                             tag : 'h3',
23914                             cls : 'roo-headline',
23915                             html : this.headline
23916                         },
23917                         {
23918                             tag : 'p',
23919                             cls : 'roo-content',
23920                             html : this.content
23921                         }
23922                     ]
23923                 }
23924             ]
23925         };
23926         
23927         if(this.icon){
23928             cfg.cn.push({
23929                 tag : 'div',
23930                 cls : 'icon',
23931                 cn :[
23932                     {
23933                         tag : 'i',
23934                         cls : 'ion ' + this.icon
23935                     }
23936                 ]
23937             });
23938         }
23939         
23940         if(this.footer){
23941             var footer = {
23942                 tag : 'a',
23943                 cls : 'small-box-footer',
23944                 href : this.fhref || '#',
23945                 html : this.footer
23946             };
23947             
23948             cfg.cn.push(footer);
23949             
23950         }
23951         
23952         return  cfg;
23953     },
23954
23955     onRender : function(ct,position){
23956         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23957
23958
23959        
23960                 
23961     },
23962
23963     setHeadline: function (value)
23964     {
23965         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23966     },
23967     
23968     setFooter: function (value, href)
23969     {
23970         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23971         
23972         if(href){
23973             this.el.select('a.small-box-footer',true).first().attr('href', href);
23974         }
23975         
23976     },
23977
23978     setContent: function (value)
23979     {
23980         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23981     },
23982
23983     initEvents: function() 
23984     {   
23985         
23986     }
23987     
23988 });
23989
23990  
23991 /*
23992  * - LGPL
23993  *
23994  * TabBox
23995  * 
23996  */
23997 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23998
23999 /**
24000  * @class Roo.bootstrap.dash.TabBox
24001  * @extends Roo.bootstrap.Component
24002  * Bootstrap TabBox class
24003  * @cfg {String} title Title of the TabBox
24004  * @cfg {String} icon Icon of the TabBox
24005  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24006  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24007  * 
24008  * @constructor
24009  * Create a new TabBox
24010  * @param {Object} config The config object
24011  */
24012
24013
24014 Roo.bootstrap.dash.TabBox = function(config){
24015     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24016     this.addEvents({
24017         // raw events
24018         /**
24019          * @event addpane
24020          * When a pane is added
24021          * @param {Roo.bootstrap.dash.TabPane} pane
24022          */
24023         "addpane" : true,
24024         /**
24025          * @event activatepane
24026          * When a pane is activated
24027          * @param {Roo.bootstrap.dash.TabPane} pane
24028          */
24029         "activatepane" : true
24030         
24031          
24032     });
24033     
24034     this.panes = [];
24035 };
24036
24037 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24038
24039     title : '',
24040     icon : false,
24041     showtabs : true,
24042     tabScrollable : false,
24043     
24044     getChildContainer : function()
24045     {
24046         return this.el.select('.tab-content', true).first();
24047     },
24048     
24049     getAutoCreate : function(){
24050         
24051         var header = {
24052             tag: 'li',
24053             cls: 'pull-left header',
24054             html: this.title,
24055             cn : []
24056         };
24057         
24058         if(this.icon){
24059             header.cn.push({
24060                 tag: 'i',
24061                 cls: 'fa ' + this.icon
24062             });
24063         }
24064         
24065         var h = {
24066             tag: 'ul',
24067             cls: 'nav nav-tabs pull-right',
24068             cn: [
24069                 header
24070             ]
24071         };
24072         
24073         if(this.tabScrollable){
24074             h = {
24075                 tag: 'div',
24076                 cls: 'tab-header',
24077                 cn: [
24078                     {
24079                         tag: 'ul',
24080                         cls: 'nav nav-tabs pull-right',
24081                         cn: [
24082                             header
24083                         ]
24084                     }
24085                 ]
24086             };
24087         }
24088         
24089         var cfg = {
24090             tag: 'div',
24091             cls: 'nav-tabs-custom',
24092             cn: [
24093                 h,
24094                 {
24095                     tag: 'div',
24096                     cls: 'tab-content no-padding',
24097                     cn: []
24098                 }
24099             ]
24100         };
24101
24102         return  cfg;
24103     },
24104     initEvents : function()
24105     {
24106         //Roo.log('add add pane handler');
24107         this.on('addpane', this.onAddPane, this);
24108     },
24109      /**
24110      * Updates the box title
24111      * @param {String} html to set the title to.
24112      */
24113     setTitle : function(value)
24114     {
24115         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24116     },
24117     onAddPane : function(pane)
24118     {
24119         this.panes.push(pane);
24120         //Roo.log('addpane');
24121         //Roo.log(pane);
24122         // tabs are rendere left to right..
24123         if(!this.showtabs){
24124             return;
24125         }
24126         
24127         var ctr = this.el.select('.nav-tabs', true).first();
24128          
24129          
24130         var existing = ctr.select('.nav-tab',true);
24131         var qty = existing.getCount();;
24132         
24133         
24134         var tab = ctr.createChild({
24135             tag : 'li',
24136             cls : 'nav-tab' + (qty ? '' : ' active'),
24137             cn : [
24138                 {
24139                     tag : 'a',
24140                     href:'#',
24141                     html : pane.title
24142                 }
24143             ]
24144         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24145         pane.tab = tab;
24146         
24147         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24148         if (!qty) {
24149             pane.el.addClass('active');
24150         }
24151         
24152                 
24153     },
24154     onTabClick : function(ev,un,ob,pane)
24155     {
24156         //Roo.log('tab - prev default');
24157         ev.preventDefault();
24158         
24159         
24160         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24161         pane.tab.addClass('active');
24162         //Roo.log(pane.title);
24163         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24164         // technically we should have a deactivate event.. but maybe add later.
24165         // and it should not de-activate the selected tab...
24166         this.fireEvent('activatepane', pane);
24167         pane.el.addClass('active');
24168         pane.fireEvent('activate');
24169         
24170         
24171     },
24172     
24173     getActivePane : function()
24174     {
24175         var r = false;
24176         Roo.each(this.panes, function(p) {
24177             if(p.el.hasClass('active')){
24178                 r = p;
24179                 return false;
24180             }
24181             
24182             return;
24183         });
24184         
24185         return r;
24186     }
24187     
24188     
24189 });
24190
24191  
24192 /*
24193  * - LGPL
24194  *
24195  * Tab pane
24196  * 
24197  */
24198 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24199 /**
24200  * @class Roo.bootstrap.TabPane
24201  * @extends Roo.bootstrap.Component
24202  * Bootstrap TabPane class
24203  * @cfg {Boolean} active (false | true) Default false
24204  * @cfg {String} title title of panel
24205
24206  * 
24207  * @constructor
24208  * Create a new TabPane
24209  * @param {Object} config The config object
24210  */
24211
24212 Roo.bootstrap.dash.TabPane = function(config){
24213     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24214     
24215     this.addEvents({
24216         // raw events
24217         /**
24218          * @event activate
24219          * When a pane is activated
24220          * @param {Roo.bootstrap.dash.TabPane} pane
24221          */
24222         "activate" : true
24223          
24224     });
24225 };
24226
24227 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24228     
24229     active : false,
24230     title : '',
24231     
24232     // the tabBox that this is attached to.
24233     tab : false,
24234      
24235     getAutoCreate : function() 
24236     {
24237         var cfg = {
24238             tag: 'div',
24239             cls: 'tab-pane'
24240         };
24241         
24242         if(this.active){
24243             cfg.cls += ' active';
24244         }
24245         
24246         return cfg;
24247     },
24248     initEvents  : function()
24249     {
24250         //Roo.log('trigger add pane handler');
24251         this.parent().fireEvent('addpane', this)
24252     },
24253     
24254      /**
24255      * Updates the tab title 
24256      * @param {String} html to set the title to.
24257      */
24258     setTitle: function(str)
24259     {
24260         if (!this.tab) {
24261             return;
24262         }
24263         this.title = str;
24264         this.tab.select('a', true).first().dom.innerHTML = str;
24265         
24266     }
24267     
24268     
24269     
24270 });
24271
24272  
24273
24274
24275  /*
24276  * - LGPL
24277  *
24278  * menu
24279  * 
24280  */
24281 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24282
24283 /**
24284  * @class Roo.bootstrap.menu.Menu
24285  * @extends Roo.bootstrap.Component
24286  * Bootstrap Menu class - container for Menu
24287  * @cfg {String} html Text of the menu
24288  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24289  * @cfg {String} icon Font awesome icon
24290  * @cfg {String} pos Menu align to (top | bottom) default bottom
24291  * 
24292  * 
24293  * @constructor
24294  * Create a new Menu
24295  * @param {Object} config The config object
24296  */
24297
24298
24299 Roo.bootstrap.menu.Menu = function(config){
24300     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24301     
24302     this.addEvents({
24303         /**
24304          * @event beforeshow
24305          * Fires before this menu is displayed
24306          * @param {Roo.bootstrap.menu.Menu} this
24307          */
24308         beforeshow : true,
24309         /**
24310          * @event beforehide
24311          * Fires before this menu is hidden
24312          * @param {Roo.bootstrap.menu.Menu} this
24313          */
24314         beforehide : true,
24315         /**
24316          * @event show
24317          * Fires after this menu is displayed
24318          * @param {Roo.bootstrap.menu.Menu} this
24319          */
24320         show : true,
24321         /**
24322          * @event hide
24323          * Fires after this menu is hidden
24324          * @param {Roo.bootstrap.menu.Menu} this
24325          */
24326         hide : true,
24327         /**
24328          * @event click
24329          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24330          * @param {Roo.bootstrap.menu.Menu} this
24331          * @param {Roo.EventObject} e
24332          */
24333         click : true
24334     });
24335     
24336 };
24337
24338 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24339     
24340     submenu : false,
24341     html : '',
24342     weight : 'default',
24343     icon : false,
24344     pos : 'bottom',
24345     
24346     
24347     getChildContainer : function() {
24348         if(this.isSubMenu){
24349             return this.el;
24350         }
24351         
24352         return this.el.select('ul.dropdown-menu', true).first();  
24353     },
24354     
24355     getAutoCreate : function()
24356     {
24357         var text = [
24358             {
24359                 tag : 'span',
24360                 cls : 'roo-menu-text',
24361                 html : this.html
24362             }
24363         ];
24364         
24365         if(this.icon){
24366             text.unshift({
24367                 tag : 'i',
24368                 cls : 'fa ' + this.icon
24369             })
24370         }
24371         
24372         
24373         var cfg = {
24374             tag : 'div',
24375             cls : 'btn-group',
24376             cn : [
24377                 {
24378                     tag : 'button',
24379                     cls : 'dropdown-button btn btn-' + this.weight,
24380                     cn : text
24381                 },
24382                 {
24383                     tag : 'button',
24384                     cls : 'dropdown-toggle btn btn-' + this.weight,
24385                     cn : [
24386                         {
24387                             tag : 'span',
24388                             cls : 'caret'
24389                         }
24390                     ]
24391                 },
24392                 {
24393                     tag : 'ul',
24394                     cls : 'dropdown-menu'
24395                 }
24396             ]
24397             
24398         };
24399         
24400         if(this.pos == 'top'){
24401             cfg.cls += ' dropup';
24402         }
24403         
24404         if(this.isSubMenu){
24405             cfg = {
24406                 tag : 'ul',
24407                 cls : 'dropdown-menu'
24408             }
24409         }
24410         
24411         return cfg;
24412     },
24413     
24414     onRender : function(ct, position)
24415     {
24416         this.isSubMenu = ct.hasClass('dropdown-submenu');
24417         
24418         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24419     },
24420     
24421     initEvents : function() 
24422     {
24423         if(this.isSubMenu){
24424             return;
24425         }
24426         
24427         this.hidden = true;
24428         
24429         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24430         this.triggerEl.on('click', this.onTriggerPress, this);
24431         
24432         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24433         this.buttonEl.on('click', this.onClick, this);
24434         
24435     },
24436     
24437     list : function()
24438     {
24439         if(this.isSubMenu){
24440             return this.el;
24441         }
24442         
24443         return this.el.select('ul.dropdown-menu', true).first();
24444     },
24445     
24446     onClick : function(e)
24447     {
24448         this.fireEvent("click", this, e);
24449     },
24450     
24451     onTriggerPress  : function(e)
24452     {   
24453         if (this.isVisible()) {
24454             this.hide();
24455         } else {
24456             this.show();
24457         }
24458     },
24459     
24460     isVisible : function(){
24461         return !this.hidden;
24462     },
24463     
24464     show : function()
24465     {
24466         this.fireEvent("beforeshow", this);
24467         
24468         this.hidden = false;
24469         this.el.addClass('open');
24470         
24471         Roo.get(document).on("mouseup", this.onMouseUp, this);
24472         
24473         this.fireEvent("show", this);
24474         
24475         
24476     },
24477     
24478     hide : function()
24479     {
24480         this.fireEvent("beforehide", this);
24481         
24482         this.hidden = true;
24483         this.el.removeClass('open');
24484         
24485         Roo.get(document).un("mouseup", this.onMouseUp);
24486         
24487         this.fireEvent("hide", this);
24488     },
24489     
24490     onMouseUp : function()
24491     {
24492         this.hide();
24493     }
24494     
24495 });
24496
24497  
24498  /*
24499  * - LGPL
24500  *
24501  * menu item
24502  * 
24503  */
24504 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24505
24506 /**
24507  * @class Roo.bootstrap.menu.Item
24508  * @extends Roo.bootstrap.Component
24509  * Bootstrap MenuItem class
24510  * @cfg {Boolean} submenu (true | false) default false
24511  * @cfg {String} html text of the item
24512  * @cfg {String} href the link
24513  * @cfg {Boolean} disable (true | false) default false
24514  * @cfg {Boolean} preventDefault (true | false) default true
24515  * @cfg {String} icon Font awesome icon
24516  * @cfg {String} pos Submenu align to (left | right) default right 
24517  * 
24518  * 
24519  * @constructor
24520  * Create a new Item
24521  * @param {Object} config The config object
24522  */
24523
24524
24525 Roo.bootstrap.menu.Item = function(config){
24526     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24527     this.addEvents({
24528         /**
24529          * @event mouseover
24530          * Fires when the mouse is hovering over this menu
24531          * @param {Roo.bootstrap.menu.Item} this
24532          * @param {Roo.EventObject} e
24533          */
24534         mouseover : true,
24535         /**
24536          * @event mouseout
24537          * Fires when the mouse exits this menu
24538          * @param {Roo.bootstrap.menu.Item} this
24539          * @param {Roo.EventObject} e
24540          */
24541         mouseout : true,
24542         // raw events
24543         /**
24544          * @event click
24545          * The raw click event for the entire grid.
24546          * @param {Roo.EventObject} e
24547          */
24548         click : true
24549     });
24550 };
24551
24552 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24553     
24554     submenu : false,
24555     href : '',
24556     html : '',
24557     preventDefault: true,
24558     disable : false,
24559     icon : false,
24560     pos : 'right',
24561     
24562     getAutoCreate : function()
24563     {
24564         var text = [
24565             {
24566                 tag : 'span',
24567                 cls : 'roo-menu-item-text',
24568                 html : this.html
24569             }
24570         ];
24571         
24572         if(this.icon){
24573             text.unshift({
24574                 tag : 'i',
24575                 cls : 'fa ' + this.icon
24576             })
24577         }
24578         
24579         var cfg = {
24580             tag : 'li',
24581             cn : [
24582                 {
24583                     tag : 'a',
24584                     href : this.href || '#',
24585                     cn : text
24586                 }
24587             ]
24588         };
24589         
24590         if(this.disable){
24591             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24592         }
24593         
24594         if(this.submenu){
24595             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24596             
24597             if(this.pos == 'left'){
24598                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24599             }
24600         }
24601         
24602         return cfg;
24603     },
24604     
24605     initEvents : function() 
24606     {
24607         this.el.on('mouseover', this.onMouseOver, this);
24608         this.el.on('mouseout', this.onMouseOut, this);
24609         
24610         this.el.select('a', true).first().on('click', this.onClick, this);
24611         
24612     },
24613     
24614     onClick : function(e)
24615     {
24616         if(this.preventDefault){
24617             e.preventDefault();
24618         }
24619         
24620         this.fireEvent("click", this, e);
24621     },
24622     
24623     onMouseOver : function(e)
24624     {
24625         if(this.submenu && this.pos == 'left'){
24626             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24627         }
24628         
24629         this.fireEvent("mouseover", this, e);
24630     },
24631     
24632     onMouseOut : function(e)
24633     {
24634         this.fireEvent("mouseout", this, e);
24635     }
24636 });
24637
24638  
24639
24640  /*
24641  * - LGPL
24642  *
24643  * menu separator
24644  * 
24645  */
24646 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24647
24648 /**
24649  * @class Roo.bootstrap.menu.Separator
24650  * @extends Roo.bootstrap.Component
24651  * Bootstrap Separator class
24652  * 
24653  * @constructor
24654  * Create a new Separator
24655  * @param {Object} config The config object
24656  */
24657
24658
24659 Roo.bootstrap.menu.Separator = function(config){
24660     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24661 };
24662
24663 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24664     
24665     getAutoCreate : function(){
24666         var cfg = {
24667             tag : 'li',
24668             cls: 'divider'
24669         };
24670         
24671         return cfg;
24672     }
24673    
24674 });
24675
24676  
24677
24678  /*
24679  * - LGPL
24680  *
24681  * Tooltip
24682  * 
24683  */
24684
24685 /**
24686  * @class Roo.bootstrap.Tooltip
24687  * Bootstrap Tooltip class
24688  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24689  * to determine which dom element triggers the tooltip.
24690  * 
24691  * It needs to add support for additional attributes like tooltip-position
24692  * 
24693  * @constructor
24694  * Create a new Toolti
24695  * @param {Object} config The config object
24696  */
24697
24698 Roo.bootstrap.Tooltip = function(config){
24699     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24700 };
24701
24702 Roo.apply(Roo.bootstrap.Tooltip, {
24703     /**
24704      * @function init initialize tooltip monitoring.
24705      * @static
24706      */
24707     currentEl : false,
24708     currentTip : false,
24709     currentRegion : false,
24710     
24711     //  init : delay?
24712     
24713     init : function()
24714     {
24715         Roo.get(document).on('mouseover', this.enter ,this);
24716         Roo.get(document).on('mouseout', this.leave, this);
24717          
24718         
24719         this.currentTip = new Roo.bootstrap.Tooltip();
24720     },
24721     
24722     enter : function(ev)
24723     {
24724         var dom = ev.getTarget();
24725         
24726         //Roo.log(['enter',dom]);
24727         var el = Roo.fly(dom);
24728         if (this.currentEl) {
24729             //Roo.log(dom);
24730             //Roo.log(this.currentEl);
24731             //Roo.log(this.currentEl.contains(dom));
24732             if (this.currentEl == el) {
24733                 return;
24734             }
24735             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24736                 return;
24737             }
24738
24739         }
24740         
24741         if (this.currentTip.el) {
24742             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24743         }    
24744         //Roo.log(ev);
24745         
24746         if(!el || el.dom == document){
24747             return;
24748         }
24749         
24750         var bindEl = el;
24751         
24752         // you can not look for children, as if el is the body.. then everythign is the child..
24753         if (!el.attr('tooltip')) { //
24754             if (!el.select("[tooltip]").elements.length) {
24755                 return;
24756             }
24757             // is the mouse over this child...?
24758             bindEl = el.select("[tooltip]").first();
24759             var xy = ev.getXY();
24760             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24761                 //Roo.log("not in region.");
24762                 return;
24763             }
24764             //Roo.log("child element over..");
24765             
24766         }
24767         this.currentEl = bindEl;
24768         this.currentTip.bind(bindEl);
24769         this.currentRegion = Roo.lib.Region.getRegion(dom);
24770         this.currentTip.enter();
24771         
24772     },
24773     leave : function(ev)
24774     {
24775         var dom = ev.getTarget();
24776         //Roo.log(['leave',dom]);
24777         if (!this.currentEl) {
24778             return;
24779         }
24780         
24781         
24782         if (dom != this.currentEl.dom) {
24783             return;
24784         }
24785         var xy = ev.getXY();
24786         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24787             return;
24788         }
24789         // only activate leave if mouse cursor is outside... bounding box..
24790         
24791         
24792         
24793         
24794         if (this.currentTip) {
24795             this.currentTip.leave();
24796         }
24797         //Roo.log('clear currentEl');
24798         this.currentEl = false;
24799         
24800         
24801     },
24802     alignment : {
24803         'left' : ['r-l', [-2,0], 'right'],
24804         'right' : ['l-r', [2,0], 'left'],
24805         'bottom' : ['t-b', [0,2], 'top'],
24806         'top' : [ 'b-t', [0,-2], 'bottom']
24807     }
24808     
24809 });
24810
24811
24812 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24813     
24814     
24815     bindEl : false,
24816     
24817     delay : null, // can be { show : 300 , hide: 500}
24818     
24819     timeout : null,
24820     
24821     hoverState : null, //???
24822     
24823     placement : 'bottom', 
24824     
24825     getAutoCreate : function(){
24826     
24827         var cfg = {
24828            cls : 'tooltip',
24829            role : 'tooltip',
24830            cn : [
24831                 {
24832                     cls : 'tooltip-arrow'
24833                 },
24834                 {
24835                     cls : 'tooltip-inner'
24836                 }
24837            ]
24838         };
24839         
24840         return cfg;
24841     },
24842     bind : function(el)
24843     {
24844         this.bindEl = el;
24845     },
24846       
24847     
24848     enter : function () {
24849        
24850         if (this.timeout != null) {
24851             clearTimeout(this.timeout);
24852         }
24853         
24854         this.hoverState = 'in';
24855          //Roo.log("enter - show");
24856         if (!this.delay || !this.delay.show) {
24857             this.show();
24858             return;
24859         }
24860         var _t = this;
24861         this.timeout = setTimeout(function () {
24862             if (_t.hoverState == 'in') {
24863                 _t.show();
24864             }
24865         }, this.delay.show);
24866     },
24867     leave : function()
24868     {
24869         clearTimeout(this.timeout);
24870     
24871         this.hoverState = 'out';
24872          if (!this.delay || !this.delay.hide) {
24873             this.hide();
24874             return;
24875         }
24876        
24877         var _t = this;
24878         this.timeout = setTimeout(function () {
24879             //Roo.log("leave - timeout");
24880             
24881             if (_t.hoverState == 'out') {
24882                 _t.hide();
24883                 Roo.bootstrap.Tooltip.currentEl = false;
24884             }
24885         }, delay);
24886     },
24887     
24888     show : function ()
24889     {
24890         if (!this.el) {
24891             this.render(document.body);
24892         }
24893         // set content.
24894         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24895         
24896         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24897         
24898         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24899         
24900         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24901         
24902         var placement = typeof this.placement == 'function' ?
24903             this.placement.call(this, this.el, on_el) :
24904             this.placement;
24905             
24906         var autoToken = /\s?auto?\s?/i;
24907         var autoPlace = autoToken.test(placement);
24908         if (autoPlace) {
24909             placement = placement.replace(autoToken, '') || 'top';
24910         }
24911         
24912         //this.el.detach()
24913         //this.el.setXY([0,0]);
24914         this.el.show();
24915         //this.el.dom.style.display='block';
24916         
24917         //this.el.appendTo(on_el);
24918         
24919         var p = this.getPosition();
24920         var box = this.el.getBox();
24921         
24922         if (autoPlace) {
24923             // fixme..
24924         }
24925         
24926         var align = Roo.bootstrap.Tooltip.alignment[placement];
24927         
24928         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24929         
24930         if(placement == 'top' || placement == 'bottom'){
24931             if(xy[0] < 0){
24932                 placement = 'right';
24933             }
24934             
24935             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24936                 placement = 'left';
24937             }
24938             
24939             var scroll = Roo.select('body', true).first().getScroll();
24940             
24941             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24942                 placement = 'top';
24943             }
24944             
24945         }
24946         
24947         align = Roo.bootstrap.Tooltip.alignment[placement];
24948         
24949         this.el.alignTo(this.bindEl, align[0],align[1]);
24950         //var arrow = this.el.select('.arrow',true).first();
24951         //arrow.set(align[2], 
24952         
24953         this.el.addClass(placement);
24954         
24955         this.el.addClass('in fade');
24956         
24957         this.hoverState = null;
24958         
24959         if (this.el.hasClass('fade')) {
24960             // fade it?
24961         }
24962         
24963     },
24964     hide : function()
24965     {
24966          
24967         if (!this.el) {
24968             return;
24969         }
24970         //this.el.setXY([0,0]);
24971         this.el.removeClass('in');
24972         //this.el.hide();
24973         
24974     }
24975     
24976 });
24977  
24978
24979  /*
24980  * - LGPL
24981  *
24982  * Location Picker
24983  * 
24984  */
24985
24986 /**
24987  * @class Roo.bootstrap.LocationPicker
24988  * @extends Roo.bootstrap.Component
24989  * Bootstrap LocationPicker class
24990  * @cfg {Number} latitude Position when init default 0
24991  * @cfg {Number} longitude Position when init default 0
24992  * @cfg {Number} zoom default 15
24993  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24994  * @cfg {Boolean} mapTypeControl default false
24995  * @cfg {Boolean} disableDoubleClickZoom default false
24996  * @cfg {Boolean} scrollwheel default true
24997  * @cfg {Boolean} streetViewControl default false
24998  * @cfg {Number} radius default 0
24999  * @cfg {String} locationName
25000  * @cfg {Boolean} draggable default true
25001  * @cfg {Boolean} enableAutocomplete default false
25002  * @cfg {Boolean} enableReverseGeocode default true
25003  * @cfg {String} markerTitle
25004  * 
25005  * @constructor
25006  * Create a new LocationPicker
25007  * @param {Object} config The config object
25008  */
25009
25010
25011 Roo.bootstrap.LocationPicker = function(config){
25012     
25013     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25014     
25015     this.addEvents({
25016         /**
25017          * @event initial
25018          * Fires when the picker initialized.
25019          * @param {Roo.bootstrap.LocationPicker} this
25020          * @param {Google Location} location
25021          */
25022         initial : true,
25023         /**
25024          * @event positionchanged
25025          * Fires when the picker position changed.
25026          * @param {Roo.bootstrap.LocationPicker} this
25027          * @param {Google Location} location
25028          */
25029         positionchanged : true,
25030         /**
25031          * @event resize
25032          * Fires when the map resize.
25033          * @param {Roo.bootstrap.LocationPicker} this
25034          */
25035         resize : true,
25036         /**
25037          * @event show
25038          * Fires when the map show.
25039          * @param {Roo.bootstrap.LocationPicker} this
25040          */
25041         show : true,
25042         /**
25043          * @event hide
25044          * Fires when the map hide.
25045          * @param {Roo.bootstrap.LocationPicker} this
25046          */
25047         hide : true,
25048         /**
25049          * @event mapClick
25050          * Fires when click the map.
25051          * @param {Roo.bootstrap.LocationPicker} this
25052          * @param {Map event} e
25053          */
25054         mapClick : true,
25055         /**
25056          * @event mapRightClick
25057          * Fires when right click the map.
25058          * @param {Roo.bootstrap.LocationPicker} this
25059          * @param {Map event} e
25060          */
25061         mapRightClick : true,
25062         /**
25063          * @event markerClick
25064          * Fires when click the marker.
25065          * @param {Roo.bootstrap.LocationPicker} this
25066          * @param {Map event} e
25067          */
25068         markerClick : true,
25069         /**
25070          * @event markerRightClick
25071          * Fires when right click the marker.
25072          * @param {Roo.bootstrap.LocationPicker} this
25073          * @param {Map event} e
25074          */
25075         markerRightClick : true,
25076         /**
25077          * @event OverlayViewDraw
25078          * Fires when OverlayView Draw
25079          * @param {Roo.bootstrap.LocationPicker} this
25080          */
25081         OverlayViewDraw : true,
25082         /**
25083          * @event OverlayViewOnAdd
25084          * Fires when OverlayView Draw
25085          * @param {Roo.bootstrap.LocationPicker} this
25086          */
25087         OverlayViewOnAdd : true,
25088         /**
25089          * @event OverlayViewOnRemove
25090          * Fires when OverlayView Draw
25091          * @param {Roo.bootstrap.LocationPicker} this
25092          */
25093         OverlayViewOnRemove : true,
25094         /**
25095          * @event OverlayViewShow
25096          * Fires when OverlayView Draw
25097          * @param {Roo.bootstrap.LocationPicker} this
25098          * @param {Pixel} cpx
25099          */
25100         OverlayViewShow : true,
25101         /**
25102          * @event OverlayViewHide
25103          * Fires when OverlayView Draw
25104          * @param {Roo.bootstrap.LocationPicker} this
25105          */
25106         OverlayViewHide : true,
25107         /**
25108          * @event loadexception
25109          * Fires when load google lib failed.
25110          * @param {Roo.bootstrap.LocationPicker} this
25111          */
25112         loadexception : true
25113     });
25114         
25115 };
25116
25117 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25118     
25119     gMapContext: false,
25120     
25121     latitude: 0,
25122     longitude: 0,
25123     zoom: 15,
25124     mapTypeId: false,
25125     mapTypeControl: false,
25126     disableDoubleClickZoom: false,
25127     scrollwheel: true,
25128     streetViewControl: false,
25129     radius: 0,
25130     locationName: '',
25131     draggable: true,
25132     enableAutocomplete: false,
25133     enableReverseGeocode: true,
25134     markerTitle: '',
25135     
25136     getAutoCreate: function()
25137     {
25138
25139         var cfg = {
25140             tag: 'div',
25141             cls: 'roo-location-picker'
25142         };
25143         
25144         return cfg
25145     },
25146     
25147     initEvents: function(ct, position)
25148     {       
25149         if(!this.el.getWidth() || this.isApplied()){
25150             return;
25151         }
25152         
25153         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25154         
25155         this.initial();
25156     },
25157     
25158     initial: function()
25159     {
25160         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25161             this.fireEvent('loadexception', this);
25162             return;
25163         }
25164         
25165         if(!this.mapTypeId){
25166             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25167         }
25168         
25169         this.gMapContext = this.GMapContext();
25170         
25171         this.initOverlayView();
25172         
25173         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25174         
25175         var _this = this;
25176                 
25177         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25178             _this.setPosition(_this.gMapContext.marker.position);
25179         });
25180         
25181         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25182             _this.fireEvent('mapClick', this, event);
25183             
25184         });
25185
25186         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25187             _this.fireEvent('mapRightClick', this, event);
25188             
25189         });
25190         
25191         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25192             _this.fireEvent('markerClick', this, event);
25193             
25194         });
25195
25196         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25197             _this.fireEvent('markerRightClick', this, event);
25198             
25199         });
25200         
25201         this.setPosition(this.gMapContext.location);
25202         
25203         this.fireEvent('initial', this, this.gMapContext.location);
25204     },
25205     
25206     initOverlayView: function()
25207     {
25208         var _this = this;
25209         
25210         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25211             
25212             draw: function()
25213             {
25214                 _this.fireEvent('OverlayViewDraw', _this);
25215             },
25216             
25217             onAdd: function()
25218             {
25219                 _this.fireEvent('OverlayViewOnAdd', _this);
25220             },
25221             
25222             onRemove: function()
25223             {
25224                 _this.fireEvent('OverlayViewOnRemove', _this);
25225             },
25226             
25227             show: function(cpx)
25228             {
25229                 _this.fireEvent('OverlayViewShow', _this, cpx);
25230             },
25231             
25232             hide: function()
25233             {
25234                 _this.fireEvent('OverlayViewHide', _this);
25235             }
25236             
25237         });
25238     },
25239     
25240     fromLatLngToContainerPixel: function(event)
25241     {
25242         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25243     },
25244     
25245     isApplied: function() 
25246     {
25247         return this.getGmapContext() == false ? false : true;
25248     },
25249     
25250     getGmapContext: function() 
25251     {
25252         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25253     },
25254     
25255     GMapContext: function() 
25256     {
25257         var position = new google.maps.LatLng(this.latitude, this.longitude);
25258         
25259         var _map = new google.maps.Map(this.el.dom, {
25260             center: position,
25261             zoom: this.zoom,
25262             mapTypeId: this.mapTypeId,
25263             mapTypeControl: this.mapTypeControl,
25264             disableDoubleClickZoom: this.disableDoubleClickZoom,
25265             scrollwheel: this.scrollwheel,
25266             streetViewControl: this.streetViewControl,
25267             locationName: this.locationName,
25268             draggable: this.draggable,
25269             enableAutocomplete: this.enableAutocomplete,
25270             enableReverseGeocode: this.enableReverseGeocode
25271         });
25272         
25273         var _marker = new google.maps.Marker({
25274             position: position,
25275             map: _map,
25276             title: this.markerTitle,
25277             draggable: this.draggable
25278         });
25279         
25280         return {
25281             map: _map,
25282             marker: _marker,
25283             circle: null,
25284             location: position,
25285             radius: this.radius,
25286             locationName: this.locationName,
25287             addressComponents: {
25288                 formatted_address: null,
25289                 addressLine1: null,
25290                 addressLine2: null,
25291                 streetName: null,
25292                 streetNumber: null,
25293                 city: null,
25294                 district: null,
25295                 state: null,
25296                 stateOrProvince: null
25297             },
25298             settings: this,
25299             domContainer: this.el.dom,
25300             geodecoder: new google.maps.Geocoder()
25301         };
25302     },
25303     
25304     drawCircle: function(center, radius, options) 
25305     {
25306         if (this.gMapContext.circle != null) {
25307             this.gMapContext.circle.setMap(null);
25308         }
25309         if (radius > 0) {
25310             radius *= 1;
25311             options = Roo.apply({}, options, {
25312                 strokeColor: "#0000FF",
25313                 strokeOpacity: .35,
25314                 strokeWeight: 2,
25315                 fillColor: "#0000FF",
25316                 fillOpacity: .2
25317             });
25318             
25319             options.map = this.gMapContext.map;
25320             options.radius = radius;
25321             options.center = center;
25322             this.gMapContext.circle = new google.maps.Circle(options);
25323             return this.gMapContext.circle;
25324         }
25325         
25326         return null;
25327     },
25328     
25329     setPosition: function(location) 
25330     {
25331         this.gMapContext.location = location;
25332         this.gMapContext.marker.setPosition(location);
25333         this.gMapContext.map.panTo(location);
25334         this.drawCircle(location, this.gMapContext.radius, {});
25335         
25336         var _this = this;
25337         
25338         if (this.gMapContext.settings.enableReverseGeocode) {
25339             this.gMapContext.geodecoder.geocode({
25340                 latLng: this.gMapContext.location
25341             }, function(results, status) {
25342                 
25343                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25344                     _this.gMapContext.locationName = results[0].formatted_address;
25345                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25346                     
25347                     _this.fireEvent('positionchanged', this, location);
25348                 }
25349             });
25350             
25351             return;
25352         }
25353         
25354         this.fireEvent('positionchanged', this, location);
25355     },
25356     
25357     resize: function()
25358     {
25359         google.maps.event.trigger(this.gMapContext.map, "resize");
25360         
25361         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25362         
25363         this.fireEvent('resize', this);
25364     },
25365     
25366     setPositionByLatLng: function(latitude, longitude)
25367     {
25368         this.setPosition(new google.maps.LatLng(latitude, longitude));
25369     },
25370     
25371     getCurrentPosition: function() 
25372     {
25373         return {
25374             latitude: this.gMapContext.location.lat(),
25375             longitude: this.gMapContext.location.lng()
25376         };
25377     },
25378     
25379     getAddressName: function() 
25380     {
25381         return this.gMapContext.locationName;
25382     },
25383     
25384     getAddressComponents: function() 
25385     {
25386         return this.gMapContext.addressComponents;
25387     },
25388     
25389     address_component_from_google_geocode: function(address_components) 
25390     {
25391         var result = {};
25392         
25393         for (var i = 0; i < address_components.length; i++) {
25394             var component = address_components[i];
25395             if (component.types.indexOf("postal_code") >= 0) {
25396                 result.postalCode = component.short_name;
25397             } else if (component.types.indexOf("street_number") >= 0) {
25398                 result.streetNumber = component.short_name;
25399             } else if (component.types.indexOf("route") >= 0) {
25400                 result.streetName = component.short_name;
25401             } else if (component.types.indexOf("neighborhood") >= 0) {
25402                 result.city = component.short_name;
25403             } else if (component.types.indexOf("locality") >= 0) {
25404                 result.city = component.short_name;
25405             } else if (component.types.indexOf("sublocality") >= 0) {
25406                 result.district = component.short_name;
25407             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25408                 result.stateOrProvince = component.short_name;
25409             } else if (component.types.indexOf("country") >= 0) {
25410                 result.country = component.short_name;
25411             }
25412         }
25413         
25414         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25415         result.addressLine2 = "";
25416         return result;
25417     },
25418     
25419     setZoomLevel: function(zoom)
25420     {
25421         this.gMapContext.map.setZoom(zoom);
25422     },
25423     
25424     show: function()
25425     {
25426         if(!this.el){
25427             return;
25428         }
25429         
25430         this.el.show();
25431         
25432         this.resize();
25433         
25434         this.fireEvent('show', this);
25435     },
25436     
25437     hide: function()
25438     {
25439         if(!this.el){
25440             return;
25441         }
25442         
25443         this.el.hide();
25444         
25445         this.fireEvent('hide', this);
25446     }
25447     
25448 });
25449
25450 Roo.apply(Roo.bootstrap.LocationPicker, {
25451     
25452     OverlayView : function(map, options)
25453     {
25454         options = options || {};
25455         
25456         this.setMap(map);
25457     }
25458     
25459     
25460 });/*
25461  * - LGPL
25462  *
25463  * Alert
25464  * 
25465  */
25466
25467 /**
25468  * @class Roo.bootstrap.Alert
25469  * @extends Roo.bootstrap.Component
25470  * Bootstrap Alert class
25471  * @cfg {String} title The title of alert
25472  * @cfg {String} html The content of alert
25473  * @cfg {String} weight (  success | info | warning | danger )
25474  * @cfg {String} faicon font-awesomeicon
25475  * 
25476  * @constructor
25477  * Create a new alert
25478  * @param {Object} config The config object
25479  */
25480
25481
25482 Roo.bootstrap.Alert = function(config){
25483     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25484     
25485 };
25486
25487 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25488     
25489     title: '',
25490     html: '',
25491     weight: false,
25492     faicon: false,
25493     
25494     getAutoCreate : function()
25495     {
25496         
25497         var cfg = {
25498             tag : 'div',
25499             cls : 'alert',
25500             cn : [
25501                 {
25502                     tag : 'i',
25503                     cls : 'roo-alert-icon'
25504                     
25505                 },
25506                 {
25507                     tag : 'b',
25508                     cls : 'roo-alert-title',
25509                     html : this.title
25510                 },
25511                 {
25512                     tag : 'span',
25513                     cls : 'roo-alert-text',
25514                     html : this.html
25515                 }
25516             ]
25517         };
25518         
25519         if(this.faicon){
25520             cfg.cn[0].cls += ' fa ' + this.faicon;
25521         }
25522         
25523         if(this.weight){
25524             cfg.cls += ' alert-' + this.weight;
25525         }
25526         
25527         return cfg;
25528     },
25529     
25530     initEvents: function() 
25531     {
25532         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25533     },
25534     
25535     setTitle : function(str)
25536     {
25537         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25538     },
25539     
25540     setText : function(str)
25541     {
25542         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25543     },
25544     
25545     setWeight : function(weight)
25546     {
25547         if(this.weight){
25548             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25549         }
25550         
25551         this.weight = weight;
25552         
25553         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25554     },
25555     
25556     setIcon : function(icon)
25557     {
25558         if(this.faicon){
25559             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25560         }
25561         
25562         this.faicon = icon;
25563         
25564         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25565     },
25566     
25567     hide: function() 
25568     {
25569         this.el.hide();   
25570     },
25571     
25572     show: function() 
25573     {  
25574         this.el.show();   
25575     }
25576     
25577 });
25578
25579  
25580 /*
25581 * Licence: LGPL
25582 */
25583
25584 /**
25585  * @class Roo.bootstrap.UploadCropbox
25586  * @extends Roo.bootstrap.Component
25587  * Bootstrap UploadCropbox class
25588  * @cfg {String} emptyText show when image has been loaded
25589  * @cfg {String} rotateNotify show when image too small to rotate
25590  * @cfg {Number} errorTimeout default 3000
25591  * @cfg {Number} minWidth default 300
25592  * @cfg {Number} minHeight default 300
25593  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25594  * @cfg {Boolean} isDocument (true|false) default false
25595  * @cfg {String} url action url
25596  * @cfg {String} paramName default 'imageUpload'
25597  * @cfg {String} method default POST
25598  * @cfg {Boolean} loadMask (true|false) default true
25599  * @cfg {Boolean} loadingText default 'Loading...'
25600  * 
25601  * @constructor
25602  * Create a new UploadCropbox
25603  * @param {Object} config The config object
25604  */
25605
25606 Roo.bootstrap.UploadCropbox = function(config){
25607     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25608     
25609     this.addEvents({
25610         /**
25611          * @event beforeselectfile
25612          * Fire before select file
25613          * @param {Roo.bootstrap.UploadCropbox} this
25614          */
25615         "beforeselectfile" : true,
25616         /**
25617          * @event initial
25618          * Fire after initEvent
25619          * @param {Roo.bootstrap.UploadCropbox} this
25620          */
25621         "initial" : true,
25622         /**
25623          * @event crop
25624          * Fire after initEvent
25625          * @param {Roo.bootstrap.UploadCropbox} this
25626          * @param {String} data
25627          */
25628         "crop" : true,
25629         /**
25630          * @event prepare
25631          * Fire when preparing the file data
25632          * @param {Roo.bootstrap.UploadCropbox} this
25633          * @param {Object} file
25634          */
25635         "prepare" : true,
25636         /**
25637          * @event exception
25638          * Fire when get exception
25639          * @param {Roo.bootstrap.UploadCropbox} this
25640          * @param {XMLHttpRequest} xhr
25641          */
25642         "exception" : true,
25643         /**
25644          * @event beforeloadcanvas
25645          * Fire before load the canvas
25646          * @param {Roo.bootstrap.UploadCropbox} this
25647          * @param {String} src
25648          */
25649         "beforeloadcanvas" : true,
25650         /**
25651          * @event trash
25652          * Fire when trash image
25653          * @param {Roo.bootstrap.UploadCropbox} this
25654          */
25655         "trash" : true,
25656         /**
25657          * @event download
25658          * Fire when download the image
25659          * @param {Roo.bootstrap.UploadCropbox} this
25660          */
25661         "download" : true,
25662         /**
25663          * @event footerbuttonclick
25664          * Fire when footerbuttonclick
25665          * @param {Roo.bootstrap.UploadCropbox} this
25666          * @param {String} type
25667          */
25668         "footerbuttonclick" : true,
25669         /**
25670          * @event resize
25671          * Fire when resize
25672          * @param {Roo.bootstrap.UploadCropbox} this
25673          */
25674         "resize" : true,
25675         /**
25676          * @event rotate
25677          * Fire when rotate the image
25678          * @param {Roo.bootstrap.UploadCropbox} this
25679          * @param {String} pos
25680          */
25681         "rotate" : true,
25682         /**
25683          * @event inspect
25684          * Fire when inspect the file
25685          * @param {Roo.bootstrap.UploadCropbox} this
25686          * @param {Object} file
25687          */
25688         "inspect" : true,
25689         /**
25690          * @event upload
25691          * Fire when xhr upload the file
25692          * @param {Roo.bootstrap.UploadCropbox} this
25693          * @param {Object} data
25694          */
25695         "upload" : true,
25696         /**
25697          * @event arrange
25698          * Fire when arrange the file data
25699          * @param {Roo.bootstrap.UploadCropbox} this
25700          * @param {Object} formData
25701          */
25702         "arrange" : true
25703     });
25704     
25705     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25706 };
25707
25708 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25709     
25710     emptyText : 'Click to upload image',
25711     rotateNotify : 'Image is too small to rotate',
25712     errorTimeout : 3000,
25713     scale : 0,
25714     baseScale : 1,
25715     rotate : 0,
25716     dragable : false,
25717     pinching : false,
25718     mouseX : 0,
25719     mouseY : 0,
25720     cropData : false,
25721     minWidth : 300,
25722     minHeight : 300,
25723     file : false,
25724     exif : {},
25725     baseRotate : 1,
25726     cropType : 'image/jpeg',
25727     buttons : false,
25728     canvasLoaded : false,
25729     isDocument : false,
25730     method : 'POST',
25731     paramName : 'imageUpload',
25732     loadMask : true,
25733     loadingText : 'Loading...',
25734     maskEl : false,
25735     
25736     getAutoCreate : function()
25737     {
25738         var cfg = {
25739             tag : 'div',
25740             cls : 'roo-upload-cropbox',
25741             cn : [
25742                 {
25743                     tag : 'input',
25744                     cls : 'roo-upload-cropbox-selector',
25745                     type : 'file'
25746                 },
25747                 {
25748                     tag : 'div',
25749                     cls : 'roo-upload-cropbox-body',
25750                     style : 'cursor:pointer',
25751                     cn : [
25752                         {
25753                             tag : 'div',
25754                             cls : 'roo-upload-cropbox-preview'
25755                         },
25756                         {
25757                             tag : 'div',
25758                             cls : 'roo-upload-cropbox-thumb'
25759                         },
25760                         {
25761                             tag : 'div',
25762                             cls : 'roo-upload-cropbox-empty-notify',
25763                             html : this.emptyText
25764                         },
25765                         {
25766                             tag : 'div',
25767                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25768                             html : this.rotateNotify
25769                         }
25770                     ]
25771                 },
25772                 {
25773                     tag : 'div',
25774                     cls : 'roo-upload-cropbox-footer',
25775                     cn : {
25776                         tag : 'div',
25777                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25778                         cn : []
25779                     }
25780                 }
25781             ]
25782         };
25783         
25784         return cfg;
25785     },
25786     
25787     onRender : function(ct, position)
25788     {
25789         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25790         
25791         if (this.buttons.length) {
25792             
25793             Roo.each(this.buttons, function(bb) {
25794                 
25795                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25796                 
25797                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25798                 
25799             }, this);
25800         }
25801         
25802         if(this.loadMask){
25803             this.maskEl = this.el;
25804         }
25805     },
25806     
25807     initEvents : function()
25808     {
25809         this.urlAPI = (window.createObjectURL && window) || 
25810                                 (window.URL && URL.revokeObjectURL && URL) || 
25811                                 (window.webkitURL && webkitURL);
25812                         
25813         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25814         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25815         
25816         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25817         this.selectorEl.hide();
25818         
25819         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25820         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25821         
25822         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25823         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25824         this.thumbEl.hide();
25825         
25826         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25827         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25828         
25829         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25830         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25831         this.errorEl.hide();
25832         
25833         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25834         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25835         this.footerEl.hide();
25836         
25837         this.setThumbBoxSize();
25838         
25839         this.bind();
25840         
25841         this.resize();
25842         
25843         this.fireEvent('initial', this);
25844     },
25845
25846     bind : function()
25847     {
25848         var _this = this;
25849         
25850         window.addEventListener("resize", function() { _this.resize(); } );
25851         
25852         this.bodyEl.on('click', this.beforeSelectFile, this);
25853         
25854         if(Roo.isTouch){
25855             this.bodyEl.on('touchstart', this.onTouchStart, this);
25856             this.bodyEl.on('touchmove', this.onTouchMove, this);
25857             this.bodyEl.on('touchend', this.onTouchEnd, this);
25858         }
25859         
25860         if(!Roo.isTouch){
25861             this.bodyEl.on('mousedown', this.onMouseDown, this);
25862             this.bodyEl.on('mousemove', this.onMouseMove, this);
25863             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25864             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25865             Roo.get(document).on('mouseup', this.onMouseUp, this);
25866         }
25867         
25868         this.selectorEl.on('change', this.onFileSelected, this);
25869     },
25870     
25871     reset : function()
25872     {    
25873         this.scale = 0;
25874         this.baseScale = 1;
25875         this.rotate = 0;
25876         this.baseRotate = 1;
25877         this.dragable = false;
25878         this.pinching = false;
25879         this.mouseX = 0;
25880         this.mouseY = 0;
25881         this.cropData = false;
25882         this.notifyEl.dom.innerHTML = this.emptyText;
25883         
25884         this.selectorEl.dom.value = '';
25885         
25886     },
25887     
25888     resize : function()
25889     {
25890         if(this.fireEvent('resize', this) != false){
25891             this.setThumbBoxPosition();
25892             this.setCanvasPosition();
25893         }
25894     },
25895     
25896     onFooterButtonClick : function(e, el, o, type)
25897     {
25898         switch (type) {
25899             case 'rotate-left' :
25900                 this.onRotateLeft(e);
25901                 break;
25902             case 'rotate-right' :
25903                 this.onRotateRight(e);
25904                 break;
25905             case 'picture' :
25906                 this.beforeSelectFile(e);
25907                 break;
25908             case 'trash' :
25909                 this.trash(e);
25910                 break;
25911             case 'crop' :
25912                 this.crop(e);
25913                 break;
25914             case 'download' :
25915                 this.download(e);
25916                 break;
25917             default :
25918                 break;
25919         }
25920         
25921         this.fireEvent('footerbuttonclick', this, type);
25922     },
25923     
25924     beforeSelectFile : function(e)
25925     {
25926         e.preventDefault();
25927         
25928         if(this.fireEvent('beforeselectfile', this) != false){
25929             this.selectorEl.dom.click();
25930         }
25931     },
25932     
25933     onFileSelected : function(e)
25934     {
25935         e.preventDefault();
25936         
25937         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25938             return;
25939         }
25940         
25941         var file = this.selectorEl.dom.files[0];
25942         
25943         if(this.fireEvent('inspect', this, file) != false){
25944             this.prepare(file);
25945         }
25946         
25947     },
25948     
25949     trash : function(e)
25950     {
25951         this.fireEvent('trash', this);
25952     },
25953     
25954     download : function(e)
25955     {
25956         this.fireEvent('download', this);
25957     },
25958     
25959     loadCanvas : function(src)
25960     {   
25961         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25962             
25963             this.reset();
25964             
25965             this.imageEl = document.createElement('img');
25966             
25967             var _this = this;
25968             
25969             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25970             
25971             this.imageEl.src = src;
25972         }
25973     },
25974     
25975     onLoadCanvas : function()
25976     {   
25977         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25978         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25979         
25980         this.bodyEl.un('click', this.beforeSelectFile, this);
25981         
25982         this.notifyEl.hide();
25983         this.thumbEl.show();
25984         this.footerEl.show();
25985         
25986         this.baseRotateLevel();
25987         
25988         if(this.isDocument){
25989             this.setThumbBoxSize();
25990         }
25991         
25992         this.setThumbBoxPosition();
25993         
25994         this.baseScaleLevel();
25995         
25996         this.draw();
25997         
25998         this.resize();
25999         
26000         this.canvasLoaded = true;
26001         
26002         if(this.loadMask){
26003             this.maskEl.unmask();
26004         }
26005         
26006     },
26007     
26008     setCanvasPosition : function()
26009     {   
26010         if(!this.canvasEl){
26011             return;
26012         }
26013         
26014         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26015         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26016         
26017         this.previewEl.setLeft(pw);
26018         this.previewEl.setTop(ph);
26019         
26020     },
26021     
26022     onMouseDown : function(e)
26023     {   
26024         e.stopEvent();
26025         
26026         this.dragable = true;
26027         this.pinching = false;
26028         
26029         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26030             this.dragable = false;
26031             return;
26032         }
26033         
26034         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26035         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26036         
26037     },
26038     
26039     onMouseMove : function(e)
26040     {   
26041         e.stopEvent();
26042         
26043         if(!this.canvasLoaded){
26044             return;
26045         }
26046         
26047         if (!this.dragable){
26048             return;
26049         }
26050         
26051         var minX = Math.ceil(this.thumbEl.getLeft(true));
26052         var minY = Math.ceil(this.thumbEl.getTop(true));
26053         
26054         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26055         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26056         
26057         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26058         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26059         
26060         x = x - this.mouseX;
26061         y = y - this.mouseY;
26062         
26063         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26064         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26065         
26066         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26067         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26068         
26069         this.previewEl.setLeft(bgX);
26070         this.previewEl.setTop(bgY);
26071         
26072         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26073         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26074     },
26075     
26076     onMouseUp : function(e)
26077     {   
26078         e.stopEvent();
26079         
26080         this.dragable = false;
26081     },
26082     
26083     onMouseWheel : function(e)
26084     {   
26085         e.stopEvent();
26086         
26087         this.startScale = this.scale;
26088         
26089         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26090         
26091         if(!this.zoomable()){
26092             this.scale = this.startScale;
26093             return;
26094         }
26095         
26096         this.draw();
26097         
26098         return;
26099     },
26100     
26101     zoomable : function()
26102     {
26103         var minScale = this.thumbEl.getWidth() / this.minWidth;
26104         
26105         if(this.minWidth < this.minHeight){
26106             minScale = this.thumbEl.getHeight() / this.minHeight;
26107         }
26108         
26109         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26110         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26111         
26112         if(
26113                 this.isDocument &&
26114                 (this.rotate == 0 || this.rotate == 180) && 
26115                 (
26116                     width > this.imageEl.OriginWidth || 
26117                     height > this.imageEl.OriginHeight ||
26118                     (width < this.minWidth && height < this.minHeight)
26119                 )
26120         ){
26121             return false;
26122         }
26123         
26124         if(
26125                 this.isDocument &&
26126                 (this.rotate == 90 || this.rotate == 270) && 
26127                 (
26128                     width > this.imageEl.OriginWidth || 
26129                     height > this.imageEl.OriginHeight ||
26130                     (width < this.minHeight && height < this.minWidth)
26131                 )
26132         ){
26133             return false;
26134         }
26135         
26136         if(
26137                 !this.isDocument &&
26138                 (this.rotate == 0 || this.rotate == 180) && 
26139                 (
26140                     width < this.minWidth || 
26141                     width > this.imageEl.OriginWidth || 
26142                     height < this.minHeight || 
26143                     height > this.imageEl.OriginHeight
26144                 )
26145         ){
26146             return false;
26147         }
26148         
26149         if(
26150                 !this.isDocument &&
26151                 (this.rotate == 90 || this.rotate == 270) && 
26152                 (
26153                     width < this.minHeight || 
26154                     width > this.imageEl.OriginWidth || 
26155                     height < this.minWidth || 
26156                     height > this.imageEl.OriginHeight
26157                 )
26158         ){
26159             return false;
26160         }
26161         
26162         return true;
26163         
26164     },
26165     
26166     onRotateLeft : function(e)
26167     {   
26168         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26169             
26170             var minScale = this.thumbEl.getWidth() / this.minWidth;
26171             
26172             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26173             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26174             
26175             this.startScale = this.scale;
26176             
26177             while (this.getScaleLevel() < minScale){
26178             
26179                 this.scale = this.scale + 1;
26180                 
26181                 if(!this.zoomable()){
26182                     break;
26183                 }
26184                 
26185                 if(
26186                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26187                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26188                 ){
26189                     continue;
26190                 }
26191                 
26192                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26193
26194                 this.draw();
26195                 
26196                 return;
26197             }
26198             
26199             this.scale = this.startScale;
26200             
26201             this.onRotateFail();
26202             
26203             return false;
26204         }
26205         
26206         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26207
26208         if(this.isDocument){
26209             this.setThumbBoxSize();
26210             this.setThumbBoxPosition();
26211             this.setCanvasPosition();
26212         }
26213         
26214         this.draw();
26215         
26216         this.fireEvent('rotate', this, 'left');
26217         
26218     },
26219     
26220     onRotateRight : function(e)
26221     {
26222         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26223             
26224             var minScale = this.thumbEl.getWidth() / this.minWidth;
26225         
26226             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26227             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26228             
26229             this.startScale = this.scale;
26230             
26231             while (this.getScaleLevel() < minScale){
26232             
26233                 this.scale = this.scale + 1;
26234                 
26235                 if(!this.zoomable()){
26236                     break;
26237                 }
26238                 
26239                 if(
26240                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26241                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26242                 ){
26243                     continue;
26244                 }
26245                 
26246                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26247
26248                 this.draw();
26249                 
26250                 return;
26251             }
26252             
26253             this.scale = this.startScale;
26254             
26255             this.onRotateFail();
26256             
26257             return false;
26258         }
26259         
26260         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26261
26262         if(this.isDocument){
26263             this.setThumbBoxSize();
26264             this.setThumbBoxPosition();
26265             this.setCanvasPosition();
26266         }
26267         
26268         this.draw();
26269         
26270         this.fireEvent('rotate', this, 'right');
26271     },
26272     
26273     onRotateFail : function()
26274     {
26275         this.errorEl.show(true);
26276         
26277         var _this = this;
26278         
26279         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26280     },
26281     
26282     draw : function()
26283     {
26284         this.previewEl.dom.innerHTML = '';
26285         
26286         var canvasEl = document.createElement("canvas");
26287         
26288         var contextEl = canvasEl.getContext("2d");
26289         
26290         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26291         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26292         var center = this.imageEl.OriginWidth / 2;
26293         
26294         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26295             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26296             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26297             center = this.imageEl.OriginHeight / 2;
26298         }
26299         
26300         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26301         
26302         contextEl.translate(center, center);
26303         contextEl.rotate(this.rotate * Math.PI / 180);
26304
26305         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26306         
26307         this.canvasEl = document.createElement("canvas");
26308         
26309         this.contextEl = this.canvasEl.getContext("2d");
26310         
26311         switch (this.rotate) {
26312             case 0 :
26313                 
26314                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26315                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26316                 
26317                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26318                 
26319                 break;
26320             case 90 : 
26321                 
26322                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26323                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26324                 
26325                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26326                     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);
26327                     break;
26328                 }
26329                 
26330                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26331                 
26332                 break;
26333             case 180 :
26334                 
26335                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26336                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26337                 
26338                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26339                     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);
26340                     break;
26341                 }
26342                 
26343                 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);
26344                 
26345                 break;
26346             case 270 :
26347                 
26348                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26349                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26350         
26351                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26352                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26353                     break;
26354                 }
26355                 
26356                 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);
26357                 
26358                 break;
26359             default : 
26360                 break;
26361         }
26362         
26363         this.previewEl.appendChild(this.canvasEl);
26364         
26365         this.setCanvasPosition();
26366     },
26367     
26368     crop : function()
26369     {
26370         if(!this.canvasLoaded){
26371             return;
26372         }
26373         
26374         var imageCanvas = document.createElement("canvas");
26375         
26376         var imageContext = imageCanvas.getContext("2d");
26377         
26378         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26379         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26380         
26381         var center = imageCanvas.width / 2;
26382         
26383         imageContext.translate(center, center);
26384         
26385         imageContext.rotate(this.rotate * Math.PI / 180);
26386         
26387         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26388         
26389         var canvas = document.createElement("canvas");
26390         
26391         var context = canvas.getContext("2d");
26392                 
26393         canvas.width = this.minWidth;
26394         canvas.height = this.minHeight;
26395
26396         switch (this.rotate) {
26397             case 0 :
26398                 
26399                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26400                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26401                 
26402                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26403                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26404                 
26405                 var targetWidth = this.minWidth - 2 * x;
26406                 var targetHeight = this.minHeight - 2 * y;
26407                 
26408                 var scale = 1;
26409                 
26410                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26411                     scale = targetWidth / width;
26412                 }
26413                 
26414                 if(x > 0 && y == 0){
26415                     scale = targetHeight / height;
26416                 }
26417                 
26418                 if(x > 0 && y > 0){
26419                     scale = targetWidth / width;
26420                     
26421                     if(width < height){
26422                         scale = targetHeight / height;
26423                     }
26424                 }
26425                 
26426                 context.scale(scale, scale);
26427                 
26428                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26429                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26430
26431                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26432                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26433
26434                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26435                 
26436                 break;
26437             case 90 : 
26438                 
26439                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26440                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26441                 
26442                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26443                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26444                 
26445                 var targetWidth = this.minWidth - 2 * x;
26446                 var targetHeight = this.minHeight - 2 * y;
26447                 
26448                 var scale = 1;
26449                 
26450                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26451                     scale = targetWidth / width;
26452                 }
26453                 
26454                 if(x > 0 && y == 0){
26455                     scale = targetHeight / height;
26456                 }
26457                 
26458                 if(x > 0 && y > 0){
26459                     scale = targetWidth / width;
26460                     
26461                     if(width < height){
26462                         scale = targetHeight / height;
26463                     }
26464                 }
26465                 
26466                 context.scale(scale, scale);
26467                 
26468                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26469                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26470
26471                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26472                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26473                 
26474                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26475                 
26476                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26477                 
26478                 break;
26479             case 180 :
26480                 
26481                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26482                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26483                 
26484                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26485                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26486                 
26487                 var targetWidth = this.minWidth - 2 * x;
26488                 var targetHeight = this.minHeight - 2 * y;
26489                 
26490                 var scale = 1;
26491                 
26492                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26493                     scale = targetWidth / width;
26494                 }
26495                 
26496                 if(x > 0 && y == 0){
26497                     scale = targetHeight / height;
26498                 }
26499                 
26500                 if(x > 0 && y > 0){
26501                     scale = targetWidth / width;
26502                     
26503                     if(width < height){
26504                         scale = targetHeight / height;
26505                     }
26506                 }
26507                 
26508                 context.scale(scale, scale);
26509                 
26510                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26511                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26512
26513                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26514                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26515
26516                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26517                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26518                 
26519                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26520                 
26521                 break;
26522             case 270 :
26523                 
26524                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26525                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26526                 
26527                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26528                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26529                 
26530                 var targetWidth = this.minWidth - 2 * x;
26531                 var targetHeight = this.minHeight - 2 * y;
26532                 
26533                 var scale = 1;
26534                 
26535                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26536                     scale = targetWidth / width;
26537                 }
26538                 
26539                 if(x > 0 && y == 0){
26540                     scale = targetHeight / height;
26541                 }
26542                 
26543                 if(x > 0 && y > 0){
26544                     scale = targetWidth / width;
26545                     
26546                     if(width < height){
26547                         scale = targetHeight / height;
26548                     }
26549                 }
26550                 
26551                 context.scale(scale, scale);
26552                 
26553                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26554                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26555
26556                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26557                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26558                 
26559                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26560                 
26561                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26562                 
26563                 break;
26564             default : 
26565                 break;
26566         }
26567         
26568         this.cropData = canvas.toDataURL(this.cropType);
26569         
26570         if(this.fireEvent('crop', this, this.cropData) !== false){
26571             this.process(this.file, this.cropData);
26572         }
26573         
26574         return;
26575         
26576     },
26577     
26578     setThumbBoxSize : function()
26579     {
26580         var width, height;
26581         
26582         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26583             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26584             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26585             
26586             this.minWidth = width;
26587             this.minHeight = height;
26588             
26589             if(this.rotate == 90 || this.rotate == 270){
26590                 this.minWidth = height;
26591                 this.minHeight = width;
26592             }
26593         }
26594         
26595         height = 300;
26596         width = Math.ceil(this.minWidth * height / this.minHeight);
26597         
26598         if(this.minWidth > this.minHeight){
26599             width = 300;
26600             height = Math.ceil(this.minHeight * width / this.minWidth);
26601         }
26602         
26603         this.thumbEl.setStyle({
26604             width : width + 'px',
26605             height : height + 'px'
26606         });
26607
26608         return;
26609             
26610     },
26611     
26612     setThumbBoxPosition : function()
26613     {
26614         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26615         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26616         
26617         this.thumbEl.setLeft(x);
26618         this.thumbEl.setTop(y);
26619         
26620     },
26621     
26622     baseRotateLevel : function()
26623     {
26624         this.baseRotate = 1;
26625         
26626         if(
26627                 typeof(this.exif) != 'undefined' &&
26628                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26629                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26630         ){
26631             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26632         }
26633         
26634         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26635         
26636     },
26637     
26638     baseScaleLevel : function()
26639     {
26640         var width, height;
26641         
26642         if(this.isDocument){
26643             
26644             if(this.baseRotate == 6 || this.baseRotate == 8){
26645             
26646                 height = this.thumbEl.getHeight();
26647                 this.baseScale = height / this.imageEl.OriginWidth;
26648
26649                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26650                     width = this.thumbEl.getWidth();
26651                     this.baseScale = width / this.imageEl.OriginHeight;
26652                 }
26653
26654                 return;
26655             }
26656
26657             height = this.thumbEl.getHeight();
26658             this.baseScale = height / this.imageEl.OriginHeight;
26659
26660             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26661                 width = this.thumbEl.getWidth();
26662                 this.baseScale = width / this.imageEl.OriginWidth;
26663             }
26664
26665             return;
26666         }
26667         
26668         if(this.baseRotate == 6 || this.baseRotate == 8){
26669             
26670             width = this.thumbEl.getHeight();
26671             this.baseScale = width / this.imageEl.OriginHeight;
26672             
26673             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26674                 height = this.thumbEl.getWidth();
26675                 this.baseScale = height / this.imageEl.OriginHeight;
26676             }
26677             
26678             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26679                 height = this.thumbEl.getWidth();
26680                 this.baseScale = height / this.imageEl.OriginHeight;
26681                 
26682                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26683                     width = this.thumbEl.getHeight();
26684                     this.baseScale = width / this.imageEl.OriginWidth;
26685                 }
26686             }
26687             
26688             return;
26689         }
26690         
26691         width = this.thumbEl.getWidth();
26692         this.baseScale = width / this.imageEl.OriginWidth;
26693         
26694         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26695             height = this.thumbEl.getHeight();
26696             this.baseScale = height / this.imageEl.OriginHeight;
26697         }
26698         
26699         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26700             
26701             height = this.thumbEl.getHeight();
26702             this.baseScale = height / this.imageEl.OriginHeight;
26703             
26704             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26705                 width = this.thumbEl.getWidth();
26706                 this.baseScale = width / this.imageEl.OriginWidth;
26707             }
26708             
26709         }
26710         
26711         return;
26712     },
26713     
26714     getScaleLevel : function()
26715     {
26716         return this.baseScale * Math.pow(1.1, this.scale);
26717     },
26718     
26719     onTouchStart : function(e)
26720     {
26721         if(!this.canvasLoaded){
26722             this.beforeSelectFile(e);
26723             return;
26724         }
26725         
26726         var touches = e.browserEvent.touches;
26727         
26728         if(!touches){
26729             return;
26730         }
26731         
26732         if(touches.length == 1){
26733             this.onMouseDown(e);
26734             return;
26735         }
26736         
26737         if(touches.length != 2){
26738             return;
26739         }
26740         
26741         var coords = [];
26742         
26743         for(var i = 0, finger; finger = touches[i]; i++){
26744             coords.push(finger.pageX, finger.pageY);
26745         }
26746         
26747         var x = Math.pow(coords[0] - coords[2], 2);
26748         var y = Math.pow(coords[1] - coords[3], 2);
26749         
26750         this.startDistance = Math.sqrt(x + y);
26751         
26752         this.startScale = this.scale;
26753         
26754         this.pinching = true;
26755         this.dragable = false;
26756         
26757     },
26758     
26759     onTouchMove : function(e)
26760     {
26761         if(!this.pinching && !this.dragable){
26762             return;
26763         }
26764         
26765         var touches = e.browserEvent.touches;
26766         
26767         if(!touches){
26768             return;
26769         }
26770         
26771         if(this.dragable){
26772             this.onMouseMove(e);
26773             return;
26774         }
26775         
26776         var coords = [];
26777         
26778         for(var i = 0, finger; finger = touches[i]; i++){
26779             coords.push(finger.pageX, finger.pageY);
26780         }
26781         
26782         var x = Math.pow(coords[0] - coords[2], 2);
26783         var y = Math.pow(coords[1] - coords[3], 2);
26784         
26785         this.endDistance = Math.sqrt(x + y);
26786         
26787         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26788         
26789         if(!this.zoomable()){
26790             this.scale = this.startScale;
26791             return;
26792         }
26793         
26794         this.draw();
26795         
26796     },
26797     
26798     onTouchEnd : function(e)
26799     {
26800         this.pinching = false;
26801         this.dragable = false;
26802         
26803     },
26804     
26805     process : function(file, crop)
26806     {
26807         if(this.loadMask){
26808             this.maskEl.mask(this.loadingText);
26809         }
26810         
26811         this.xhr = new XMLHttpRequest();
26812         
26813         file.xhr = this.xhr;
26814
26815         this.xhr.open(this.method, this.url, true);
26816         
26817         var headers = {
26818             "Accept": "application/json",
26819             "Cache-Control": "no-cache",
26820             "X-Requested-With": "XMLHttpRequest"
26821         };
26822         
26823         for (var headerName in headers) {
26824             var headerValue = headers[headerName];
26825             if (headerValue) {
26826                 this.xhr.setRequestHeader(headerName, headerValue);
26827             }
26828         }
26829         
26830         var _this = this;
26831         
26832         this.xhr.onload = function()
26833         {
26834             _this.xhrOnLoad(_this.xhr);
26835         }
26836         
26837         this.xhr.onerror = function()
26838         {
26839             _this.xhrOnError(_this.xhr);
26840         }
26841         
26842         var formData = new FormData();
26843
26844         formData.append('returnHTML', 'NO');
26845         
26846         if(crop){
26847             formData.append('crop', crop);
26848         }
26849         
26850         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26851             formData.append(this.paramName, file, file.name);
26852         }
26853         
26854         if(typeof(file.filename) != 'undefined'){
26855             formData.append('filename', file.filename);
26856         }
26857         
26858         if(typeof(file.mimetype) != 'undefined'){
26859             formData.append('mimetype', file.mimetype);
26860         }
26861         
26862         if(this.fireEvent('arrange', this, formData) != false){
26863             this.xhr.send(formData);
26864         };
26865     },
26866     
26867     xhrOnLoad : function(xhr)
26868     {
26869         if(this.loadMask){
26870             this.maskEl.unmask();
26871         }
26872         
26873         if (xhr.readyState !== 4) {
26874             this.fireEvent('exception', this, xhr);
26875             return;
26876         }
26877
26878         var response = Roo.decode(xhr.responseText);
26879         
26880         if(!response.success){
26881             this.fireEvent('exception', this, xhr);
26882             return;
26883         }
26884         
26885         var response = Roo.decode(xhr.responseText);
26886         
26887         this.fireEvent('upload', this, response);
26888         
26889     },
26890     
26891     xhrOnError : function()
26892     {
26893         if(this.loadMask){
26894             this.maskEl.unmask();
26895         }
26896         
26897         Roo.log('xhr on error');
26898         
26899         var response = Roo.decode(xhr.responseText);
26900           
26901         Roo.log(response);
26902         
26903     },
26904     
26905     prepare : function(file)
26906     {   
26907         if(this.loadMask){
26908             this.maskEl.mask(this.loadingText);
26909         }
26910         
26911         this.file = false;
26912         this.exif = {};
26913         
26914         if(typeof(file) === 'string'){
26915             this.loadCanvas(file);
26916             return;
26917         }
26918         
26919         if(!file || !this.urlAPI){
26920             return;
26921         }
26922         
26923         this.file = file;
26924         this.cropType = file.type;
26925         
26926         var _this = this;
26927         
26928         if(this.fireEvent('prepare', this, this.file) != false){
26929             
26930             var reader = new FileReader();
26931             
26932             reader.onload = function (e) {
26933                 if (e.target.error) {
26934                     Roo.log(e.target.error);
26935                     return;
26936                 }
26937                 
26938                 var buffer = e.target.result,
26939                     dataView = new DataView(buffer),
26940                     offset = 2,
26941                     maxOffset = dataView.byteLength - 4,
26942                     markerBytes,
26943                     markerLength;
26944                 
26945                 if (dataView.getUint16(0) === 0xffd8) {
26946                     while (offset < maxOffset) {
26947                         markerBytes = dataView.getUint16(offset);
26948                         
26949                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26950                             markerLength = dataView.getUint16(offset + 2) + 2;
26951                             if (offset + markerLength > dataView.byteLength) {
26952                                 Roo.log('Invalid meta data: Invalid segment size.');
26953                                 break;
26954                             }
26955                             
26956                             if(markerBytes == 0xffe1){
26957                                 _this.parseExifData(
26958                                     dataView,
26959                                     offset,
26960                                     markerLength
26961                                 );
26962                             }
26963                             
26964                             offset += markerLength;
26965                             
26966                             continue;
26967                         }
26968                         
26969                         break;
26970                     }
26971                     
26972                 }
26973                 
26974                 var url = _this.urlAPI.createObjectURL(_this.file);
26975                 
26976                 _this.loadCanvas(url);
26977                 
26978                 return;
26979             }
26980             
26981             reader.readAsArrayBuffer(this.file);
26982             
26983         }
26984         
26985     },
26986     
26987     parseExifData : function(dataView, offset, length)
26988     {
26989         var tiffOffset = offset + 10,
26990             littleEndian,
26991             dirOffset;
26992     
26993         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26994             // No Exif data, might be XMP data instead
26995             return;
26996         }
26997         
26998         // Check for the ASCII code for "Exif" (0x45786966):
26999         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27000             // No Exif data, might be XMP data instead
27001             return;
27002         }
27003         if (tiffOffset + 8 > dataView.byteLength) {
27004             Roo.log('Invalid Exif data: Invalid segment size.');
27005             return;
27006         }
27007         // Check for the two null bytes:
27008         if (dataView.getUint16(offset + 8) !== 0x0000) {
27009             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27010             return;
27011         }
27012         // Check the byte alignment:
27013         switch (dataView.getUint16(tiffOffset)) {
27014         case 0x4949:
27015             littleEndian = true;
27016             break;
27017         case 0x4D4D:
27018             littleEndian = false;
27019             break;
27020         default:
27021             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27022             return;
27023         }
27024         // Check for the TIFF tag marker (0x002A):
27025         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27026             Roo.log('Invalid Exif data: Missing TIFF marker.');
27027             return;
27028         }
27029         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27030         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27031         
27032         this.parseExifTags(
27033             dataView,
27034             tiffOffset,
27035             tiffOffset + dirOffset,
27036             littleEndian
27037         );
27038     },
27039     
27040     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27041     {
27042         var tagsNumber,
27043             dirEndOffset,
27044             i;
27045         if (dirOffset + 6 > dataView.byteLength) {
27046             Roo.log('Invalid Exif data: Invalid directory offset.');
27047             return;
27048         }
27049         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27050         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27051         if (dirEndOffset + 4 > dataView.byteLength) {
27052             Roo.log('Invalid Exif data: Invalid directory size.');
27053             return;
27054         }
27055         for (i = 0; i < tagsNumber; i += 1) {
27056             this.parseExifTag(
27057                 dataView,
27058                 tiffOffset,
27059                 dirOffset + 2 + 12 * i, // tag offset
27060                 littleEndian
27061             );
27062         }
27063         // Return the offset to the next directory:
27064         return dataView.getUint32(dirEndOffset, littleEndian);
27065     },
27066     
27067     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27068     {
27069         var tag = dataView.getUint16(offset, littleEndian);
27070         
27071         this.exif[tag] = this.getExifValue(
27072             dataView,
27073             tiffOffset,
27074             offset,
27075             dataView.getUint16(offset + 2, littleEndian), // tag type
27076             dataView.getUint32(offset + 4, littleEndian), // tag length
27077             littleEndian
27078         );
27079     },
27080     
27081     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27082     {
27083         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27084             tagSize,
27085             dataOffset,
27086             values,
27087             i,
27088             str,
27089             c;
27090     
27091         if (!tagType) {
27092             Roo.log('Invalid Exif data: Invalid tag type.');
27093             return;
27094         }
27095         
27096         tagSize = tagType.size * length;
27097         // Determine if the value is contained in the dataOffset bytes,
27098         // or if the value at the dataOffset is a pointer to the actual data:
27099         dataOffset = tagSize > 4 ?
27100                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27101         if (dataOffset + tagSize > dataView.byteLength) {
27102             Roo.log('Invalid Exif data: Invalid data offset.');
27103             return;
27104         }
27105         if (length === 1) {
27106             return tagType.getValue(dataView, dataOffset, littleEndian);
27107         }
27108         values = [];
27109         for (i = 0; i < length; i += 1) {
27110             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27111         }
27112         
27113         if (tagType.ascii) {
27114             str = '';
27115             // Concatenate the chars:
27116             for (i = 0; i < values.length; i += 1) {
27117                 c = values[i];
27118                 // Ignore the terminating NULL byte(s):
27119                 if (c === '\u0000') {
27120                     break;
27121                 }
27122                 str += c;
27123             }
27124             return str;
27125         }
27126         return values;
27127     }
27128     
27129 });
27130
27131 Roo.apply(Roo.bootstrap.UploadCropbox, {
27132     tags : {
27133         'Orientation': 0x0112
27134     },
27135     
27136     Orientation: {
27137             1: 0, //'top-left',
27138 //            2: 'top-right',
27139             3: 180, //'bottom-right',
27140 //            4: 'bottom-left',
27141 //            5: 'left-top',
27142             6: 90, //'right-top',
27143 //            7: 'right-bottom',
27144             8: 270 //'left-bottom'
27145     },
27146     
27147     exifTagTypes : {
27148         // byte, 8-bit unsigned int:
27149         1: {
27150             getValue: function (dataView, dataOffset) {
27151                 return dataView.getUint8(dataOffset);
27152             },
27153             size: 1
27154         },
27155         // ascii, 8-bit byte:
27156         2: {
27157             getValue: function (dataView, dataOffset) {
27158                 return String.fromCharCode(dataView.getUint8(dataOffset));
27159             },
27160             size: 1,
27161             ascii: true
27162         },
27163         // short, 16 bit int:
27164         3: {
27165             getValue: function (dataView, dataOffset, littleEndian) {
27166                 return dataView.getUint16(dataOffset, littleEndian);
27167             },
27168             size: 2
27169         },
27170         // long, 32 bit int:
27171         4: {
27172             getValue: function (dataView, dataOffset, littleEndian) {
27173                 return dataView.getUint32(dataOffset, littleEndian);
27174             },
27175             size: 4
27176         },
27177         // rational = two long values, first is numerator, second is denominator:
27178         5: {
27179             getValue: function (dataView, dataOffset, littleEndian) {
27180                 return dataView.getUint32(dataOffset, littleEndian) /
27181                     dataView.getUint32(dataOffset + 4, littleEndian);
27182             },
27183             size: 8
27184         },
27185         // slong, 32 bit signed int:
27186         9: {
27187             getValue: function (dataView, dataOffset, littleEndian) {
27188                 return dataView.getInt32(dataOffset, littleEndian);
27189             },
27190             size: 4
27191         },
27192         // srational, two slongs, first is numerator, second is denominator:
27193         10: {
27194             getValue: function (dataView, dataOffset, littleEndian) {
27195                 return dataView.getInt32(dataOffset, littleEndian) /
27196                     dataView.getInt32(dataOffset + 4, littleEndian);
27197             },
27198             size: 8
27199         }
27200     },
27201     
27202     footer : {
27203         STANDARD : [
27204             {
27205                 tag : 'div',
27206                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27207                 action : 'rotate-left',
27208                 cn : [
27209                     {
27210                         tag : 'button',
27211                         cls : 'btn btn-default',
27212                         html : '<i class="fa fa-undo"></i>'
27213                     }
27214                 ]
27215             },
27216             {
27217                 tag : 'div',
27218                 cls : 'btn-group roo-upload-cropbox-picture',
27219                 action : 'picture',
27220                 cn : [
27221                     {
27222                         tag : 'button',
27223                         cls : 'btn btn-default',
27224                         html : '<i class="fa fa-picture-o"></i>'
27225                     }
27226                 ]
27227             },
27228             {
27229                 tag : 'div',
27230                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27231                 action : 'rotate-right',
27232                 cn : [
27233                     {
27234                         tag : 'button',
27235                         cls : 'btn btn-default',
27236                         html : '<i class="fa fa-repeat"></i>'
27237                     }
27238                 ]
27239             }
27240         ],
27241         DOCUMENT : [
27242             {
27243                 tag : 'div',
27244                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27245                 action : 'rotate-left',
27246                 cn : [
27247                     {
27248                         tag : 'button',
27249                         cls : 'btn btn-default',
27250                         html : '<i class="fa fa-undo"></i>'
27251                     }
27252                 ]
27253             },
27254             {
27255                 tag : 'div',
27256                 cls : 'btn-group roo-upload-cropbox-download',
27257                 action : 'download',
27258                 cn : [
27259                     {
27260                         tag : 'button',
27261                         cls : 'btn btn-default',
27262                         html : '<i class="fa fa-download"></i>'
27263                     }
27264                 ]
27265             },
27266             {
27267                 tag : 'div',
27268                 cls : 'btn-group roo-upload-cropbox-crop',
27269                 action : 'crop',
27270                 cn : [
27271                     {
27272                         tag : 'button',
27273                         cls : 'btn btn-default',
27274                         html : '<i class="fa fa-crop"></i>'
27275                     }
27276                 ]
27277             },
27278             {
27279                 tag : 'div',
27280                 cls : 'btn-group roo-upload-cropbox-trash',
27281                 action : 'trash',
27282                 cn : [
27283                     {
27284                         tag : 'button',
27285                         cls : 'btn btn-default',
27286                         html : '<i class="fa fa-trash"></i>'
27287                     }
27288                 ]
27289             },
27290             {
27291                 tag : 'div',
27292                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27293                 action : 'rotate-right',
27294                 cn : [
27295                     {
27296                         tag : 'button',
27297                         cls : 'btn btn-default',
27298                         html : '<i class="fa fa-repeat"></i>'
27299                     }
27300                 ]
27301             }
27302         ],
27303         ROTATOR : [
27304             {
27305                 tag : 'div',
27306                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27307                 action : 'rotate-left',
27308                 cn : [
27309                     {
27310                         tag : 'button',
27311                         cls : 'btn btn-default',
27312                         html : '<i class="fa fa-undo"></i>'
27313                     }
27314                 ]
27315             },
27316             {
27317                 tag : 'div',
27318                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27319                 action : 'rotate-right',
27320                 cn : [
27321                     {
27322                         tag : 'button',
27323                         cls : 'btn btn-default',
27324                         html : '<i class="fa fa-repeat"></i>'
27325                     }
27326                 ]
27327             }
27328         ]
27329     }
27330 });
27331
27332 /*
27333 * Licence: LGPL
27334 */
27335
27336 /**
27337  * @class Roo.bootstrap.DocumentManager
27338  * @extends Roo.bootstrap.Component
27339  * Bootstrap DocumentManager class
27340  * @cfg {String} paramName default 'imageUpload'
27341  * @cfg {String} method default POST
27342  * @cfg {String} url action url
27343  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27344  * @cfg {Boolean} multiple multiple upload default true
27345  * @cfg {Number} thumbSize default 300
27346  * @cfg {String} fieldLabel
27347  * @cfg {Number} labelWidth default 4
27348  * @cfg {String} labelAlign (left|top) default left
27349  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27350  * 
27351  * @constructor
27352  * Create a new DocumentManager
27353  * @param {Object} config The config object
27354  */
27355
27356 Roo.bootstrap.DocumentManager = function(config){
27357     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27358     
27359     this.addEvents({
27360         /**
27361          * @event initial
27362          * Fire when initial the DocumentManager
27363          * @param {Roo.bootstrap.DocumentManager} this
27364          */
27365         "initial" : true,
27366         /**
27367          * @event inspect
27368          * inspect selected file
27369          * @param {Roo.bootstrap.DocumentManager} this
27370          * @param {File} file
27371          */
27372         "inspect" : true,
27373         /**
27374          * @event exception
27375          * Fire when xhr load exception
27376          * @param {Roo.bootstrap.DocumentManager} this
27377          * @param {XMLHttpRequest} xhr
27378          */
27379         "exception" : true,
27380         /**
27381          * @event prepare
27382          * prepare the form data
27383          * @param {Roo.bootstrap.DocumentManager} this
27384          * @param {Object} formData
27385          */
27386         "prepare" : true,
27387         /**
27388          * @event remove
27389          * Fire when remove the file
27390          * @param {Roo.bootstrap.DocumentManager} this
27391          * @param {Object} file
27392          */
27393         "remove" : true,
27394         /**
27395          * @event refresh
27396          * Fire after refresh the file
27397          * @param {Roo.bootstrap.DocumentManager} this
27398          */
27399         "refresh" : true,
27400         /**
27401          * @event click
27402          * Fire after click the image
27403          * @param {Roo.bootstrap.DocumentManager} this
27404          * @param {Object} file
27405          */
27406         "click" : true,
27407         /**
27408          * @event edit
27409          * Fire when upload a image and editable set to true
27410          * @param {Roo.bootstrap.DocumentManager} this
27411          * @param {Object} file
27412          */
27413         "edit" : true,
27414         /**
27415          * @event beforeselectfile
27416          * Fire before select file
27417          * @param {Roo.bootstrap.DocumentManager} this
27418          */
27419         "beforeselectfile" : true,
27420         /**
27421          * @event process
27422          * Fire before process file
27423          * @param {Roo.bootstrap.DocumentManager} this
27424          * @param {Object} file
27425          */
27426         "process" : true
27427         
27428     });
27429 };
27430
27431 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27432     
27433     boxes : 0,
27434     inputName : '',
27435     thumbSize : 300,
27436     multiple : true,
27437     files : [],
27438     method : 'POST',
27439     url : '',
27440     paramName : 'imageUpload',
27441     fieldLabel : '',
27442     labelWidth : 4,
27443     labelAlign : 'left',
27444     editable : true,
27445     delegates : [],
27446     
27447     
27448     xhr : false, 
27449     
27450     getAutoCreate : function()
27451     {   
27452         var managerWidget = {
27453             tag : 'div',
27454             cls : 'roo-document-manager',
27455             cn : [
27456                 {
27457                     tag : 'input',
27458                     cls : 'roo-document-manager-selector',
27459                     type : 'file'
27460                 },
27461                 {
27462                     tag : 'div',
27463                     cls : 'roo-document-manager-uploader',
27464                     cn : [
27465                         {
27466                             tag : 'div',
27467                             cls : 'roo-document-manager-upload-btn',
27468                             html : '<i class="fa fa-plus"></i>'
27469                         }
27470                     ]
27471                     
27472                 }
27473             ]
27474         };
27475         
27476         var content = [
27477             {
27478                 tag : 'div',
27479                 cls : 'column col-md-12',
27480                 cn : managerWidget
27481             }
27482         ];
27483         
27484         if(this.fieldLabel.length){
27485             
27486             content = [
27487                 {
27488                     tag : 'div',
27489                     cls : 'column col-md-12',
27490                     html : this.fieldLabel
27491                 },
27492                 {
27493                     tag : 'div',
27494                     cls : 'column col-md-12',
27495                     cn : managerWidget
27496                 }
27497             ];
27498
27499             if(this.labelAlign == 'left'){
27500                 content = [
27501                     {
27502                         tag : 'div',
27503                         cls : 'column col-md-' + this.labelWidth,
27504                         html : this.fieldLabel
27505                     },
27506                     {
27507                         tag : 'div',
27508                         cls : 'column col-md-' + (12 - this.labelWidth),
27509                         cn : managerWidget
27510                     }
27511                 ];
27512                 
27513             }
27514         }
27515         
27516         var cfg = {
27517             tag : 'div',
27518             cls : 'row clearfix',
27519             cn : content
27520         };
27521         
27522         return cfg;
27523         
27524     },
27525     
27526     initEvents : function()
27527     {
27528         this.managerEl = this.el.select('.roo-document-manager', true).first();
27529         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27530         
27531         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27532         this.selectorEl.hide();
27533         
27534         if(this.multiple){
27535             this.selectorEl.attr('multiple', 'multiple');
27536         }
27537         
27538         this.selectorEl.on('change', this.onFileSelected, this);
27539         
27540         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27541         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27542         
27543         this.uploader.on('click', this.onUploaderClick, this);
27544         
27545         this.renderProgressDialog();
27546         
27547         var _this = this;
27548         
27549         window.addEventListener("resize", function() { _this.refresh(); } );
27550         
27551         this.fireEvent('initial', this);
27552     },
27553     
27554     renderProgressDialog : function()
27555     {
27556         var _this = this;
27557         
27558         this.progressDialog = new Roo.bootstrap.Modal({
27559             cls : 'roo-document-manager-progress-dialog',
27560             allow_close : false,
27561             title : '',
27562             buttons : [
27563                 {
27564                     name  :'cancel',
27565                     weight : 'danger',
27566                     html : 'Cancel'
27567                 }
27568             ], 
27569             listeners : { 
27570                 btnclick : function() {
27571                     _this.uploadCancel();
27572                     this.hide();
27573                 }
27574             }
27575         });
27576          
27577         this.progressDialog.render(Roo.get(document.body));
27578          
27579         this.progress = new Roo.bootstrap.Progress({
27580             cls : 'roo-document-manager-progress',
27581             active : true,
27582             striped : true
27583         });
27584         
27585         this.progress.render(this.progressDialog.getChildContainer());
27586         
27587         this.progressBar = new Roo.bootstrap.ProgressBar({
27588             cls : 'roo-document-manager-progress-bar',
27589             aria_valuenow : 0,
27590             aria_valuemin : 0,
27591             aria_valuemax : 12,
27592             panel : 'success'
27593         });
27594         
27595         this.progressBar.render(this.progress.getChildContainer());
27596     },
27597     
27598     onUploaderClick : function(e)
27599     {
27600         e.preventDefault();
27601      
27602         if(this.fireEvent('beforeselectfile', this) != false){
27603             this.selectorEl.dom.click();
27604         }
27605         
27606     },
27607     
27608     onFileSelected : function(e)
27609     {
27610         e.preventDefault();
27611         
27612         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27613             return;
27614         }
27615         
27616         Roo.each(this.selectorEl.dom.files, function(file){
27617             if(this.fireEvent('inspect', this, file) != false){
27618                 this.files.push(file);
27619             }
27620         }, this);
27621         
27622         this.queue();
27623         
27624     },
27625     
27626     queue : function()
27627     {
27628         this.selectorEl.dom.value = '';
27629         
27630         if(!this.files.length){
27631             return;
27632         }
27633         
27634         if(this.boxes > 0 && this.files.length > this.boxes){
27635             this.files = this.files.slice(0, this.boxes);
27636         }
27637         
27638         this.uploader.show();
27639         
27640         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27641             this.uploader.hide();
27642         }
27643         
27644         var _this = this;
27645         
27646         var files = [];
27647         
27648         var docs = [];
27649         
27650         Roo.each(this.files, function(file){
27651             
27652             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27653                 var f = this.renderPreview(file);
27654                 files.push(f);
27655                 return;
27656             }
27657             
27658             if(file.type.indexOf('image') != -1){
27659                 this.delegates.push(
27660                     (function(){
27661                         _this.process(file);
27662                     }).createDelegate(this)
27663                 );
27664         
27665                 return;
27666             }
27667             
27668             docs.push(
27669                 (function(){
27670                     _this.process(file);
27671                 }).createDelegate(this)
27672             );
27673             
27674         }, this);
27675         
27676         this.files = files;
27677         
27678         this.delegates = this.delegates.concat(docs);
27679         
27680         if(!this.delegates.length){
27681             this.refresh();
27682             return;
27683         }
27684         
27685         this.progressBar.aria_valuemax = this.delegates.length;
27686         
27687         this.arrange();
27688         
27689         return;
27690     },
27691     
27692     arrange : function()
27693     {
27694         if(!this.delegates.length){
27695             this.progressDialog.hide();
27696             this.refresh();
27697             return;
27698         }
27699         
27700         var delegate = this.delegates.shift();
27701         
27702         this.progressDialog.show();
27703         
27704         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27705         
27706         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27707         
27708         delegate();
27709     },
27710     
27711     refresh : function()
27712     {
27713         this.uploader.show();
27714         
27715         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27716             this.uploader.hide();
27717         }
27718         
27719         Roo.isTouch ? this.closable(false) : this.closable(true);
27720         
27721         this.fireEvent('refresh', this);
27722     },
27723     
27724     onRemove : function(e, el, o)
27725     {
27726         e.preventDefault();
27727         
27728         this.fireEvent('remove', this, o);
27729         
27730     },
27731     
27732     remove : function(o)
27733     {
27734         var files = [];
27735         
27736         Roo.each(this.files, function(file){
27737             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27738                 files.push(file);
27739                 return;
27740             }
27741
27742             o.target.remove();
27743
27744         }, this);
27745         
27746         this.files = files;
27747         
27748         this.refresh();
27749     },
27750     
27751     clear : function()
27752     {
27753         Roo.each(this.files, function(file){
27754             if(!file.target){
27755                 return;
27756             }
27757             
27758             file.target.remove();
27759
27760         }, this);
27761         
27762         this.files = [];
27763         
27764         this.refresh();
27765     },
27766     
27767     onClick : function(e, el, o)
27768     {
27769         e.preventDefault();
27770         
27771         this.fireEvent('click', this, o);
27772         
27773     },
27774     
27775     closable : function(closable)
27776     {
27777         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27778             
27779             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27780             
27781             if(closable){
27782                 el.show();
27783                 return;
27784             }
27785             
27786             el.hide();
27787             
27788         }, this);
27789     },
27790     
27791     xhrOnLoad : function(xhr)
27792     {
27793         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27794             el.remove();
27795         }, this);
27796         
27797         if (xhr.readyState !== 4) {
27798             this.arrange();
27799             this.fireEvent('exception', this, xhr);
27800             return;
27801         }
27802
27803         var response = Roo.decode(xhr.responseText);
27804         
27805         if(!response.success){
27806             this.arrange();
27807             this.fireEvent('exception', this, xhr);
27808             return;
27809         }
27810         
27811         var file = this.renderPreview(response.data);
27812         
27813         this.files.push(file);
27814         
27815         this.arrange();
27816         
27817     },
27818     
27819     xhrOnError : function(xhr)
27820     {
27821         Roo.log('xhr on error');
27822         
27823         var response = Roo.decode(xhr.responseText);
27824           
27825         Roo.log(response);
27826         
27827         this.arrange();
27828     },
27829     
27830     process : function(file)
27831     {
27832         if(this.fireEvent('process', this, file) !== false){
27833             if(this.editable && file.type.indexOf('image') != -1){
27834                 this.fireEvent('edit', this, file);
27835                 return;
27836             }
27837
27838             this.uploadStart(file, false);
27839
27840             return;
27841         }
27842         
27843     },
27844     
27845     uploadStart : function(file, crop)
27846     {
27847         this.xhr = new XMLHttpRequest();
27848         
27849         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27850             this.arrange();
27851             return;
27852         }
27853         
27854         file.xhr = this.xhr;
27855             
27856         this.managerEl.createChild({
27857             tag : 'div',
27858             cls : 'roo-document-manager-loading',
27859             cn : [
27860                 {
27861                     tag : 'div',
27862                     tooltip : file.name,
27863                     cls : 'roo-document-manager-thumb',
27864                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27865                 }
27866             ]
27867
27868         });
27869
27870         this.xhr.open(this.method, this.url, true);
27871         
27872         var headers = {
27873             "Accept": "application/json",
27874             "Cache-Control": "no-cache",
27875             "X-Requested-With": "XMLHttpRequest"
27876         };
27877         
27878         for (var headerName in headers) {
27879             var headerValue = headers[headerName];
27880             if (headerValue) {
27881                 this.xhr.setRequestHeader(headerName, headerValue);
27882             }
27883         }
27884         
27885         var _this = this;
27886         
27887         this.xhr.onload = function()
27888         {
27889             _this.xhrOnLoad(_this.xhr);
27890         }
27891         
27892         this.xhr.onerror = function()
27893         {
27894             _this.xhrOnError(_this.xhr);
27895         }
27896         
27897         var formData = new FormData();
27898
27899         formData.append('returnHTML', 'NO');
27900         
27901         if(crop){
27902             formData.append('crop', crop);
27903         }
27904         
27905         formData.append(this.paramName, file, file.name);
27906         
27907         if(this.fireEvent('prepare', this, formData) != false){
27908             this.xhr.send(formData);
27909         };
27910     },
27911     
27912     uploadCancel : function()
27913     {
27914         if (this.xhr) {
27915             this.xhr.abort();
27916         }
27917         
27918         
27919         this.delegates = [];
27920         
27921         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27922             el.remove();
27923         }, this);
27924         
27925         this.arrange();
27926     },
27927     
27928     renderPreview : function(file)
27929     {
27930         if(typeof(file.target) != 'undefined' && file.target){
27931             return file;
27932         }
27933         
27934         var previewEl = this.managerEl.createChild({
27935             tag : 'div',
27936             cls : 'roo-document-manager-preview',
27937             cn : [
27938                 {
27939                     tag : 'div',
27940                     tooltip : file.filename,
27941                     cls : 'roo-document-manager-thumb',
27942                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27943                 },
27944                 {
27945                     tag : 'button',
27946                     cls : 'close',
27947                     html : '<i class="fa fa-times-circle"></i>'
27948                 }
27949             ]
27950         });
27951
27952         var close = previewEl.select('button.close', true).first();
27953
27954         close.on('click', this.onRemove, this, file);
27955
27956         file.target = previewEl;
27957
27958         var image = previewEl.select('img', true).first();
27959         
27960         var _this = this;
27961         
27962         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27963         
27964         image.on('click', this.onClick, this, file);
27965         
27966         return file;
27967         
27968     },
27969     
27970     onPreviewLoad : function(file, image)
27971     {
27972         if(typeof(file.target) == 'undefined' || !file.target){
27973             return;
27974         }
27975         
27976         var width = image.dom.naturalWidth || image.dom.width;
27977         var height = image.dom.naturalHeight || image.dom.height;
27978         
27979         if(width > height){
27980             file.target.addClass('wide');
27981             return;
27982         }
27983         
27984         file.target.addClass('tall');
27985         return;
27986         
27987     },
27988     
27989     uploadFromSource : function(file, crop)
27990     {
27991         this.xhr = new XMLHttpRequest();
27992         
27993         this.managerEl.createChild({
27994             tag : 'div',
27995             cls : 'roo-document-manager-loading',
27996             cn : [
27997                 {
27998                     tag : 'div',
27999                     tooltip : file.name,
28000                     cls : 'roo-document-manager-thumb',
28001                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28002                 }
28003             ]
28004
28005         });
28006
28007         this.xhr.open(this.method, this.url, true);
28008         
28009         var headers = {
28010             "Accept": "application/json",
28011             "Cache-Control": "no-cache",
28012             "X-Requested-With": "XMLHttpRequest"
28013         };
28014         
28015         for (var headerName in headers) {
28016             var headerValue = headers[headerName];
28017             if (headerValue) {
28018                 this.xhr.setRequestHeader(headerName, headerValue);
28019             }
28020         }
28021         
28022         var _this = this;
28023         
28024         this.xhr.onload = function()
28025         {
28026             _this.xhrOnLoad(_this.xhr);
28027         }
28028         
28029         this.xhr.onerror = function()
28030         {
28031             _this.xhrOnError(_this.xhr);
28032         }
28033         
28034         var formData = new FormData();
28035
28036         formData.append('returnHTML', 'NO');
28037         
28038         formData.append('crop', crop);
28039         
28040         if(typeof(file.filename) != 'undefined'){
28041             formData.append('filename', file.filename);
28042         }
28043         
28044         if(typeof(file.mimetype) != 'undefined'){
28045             formData.append('mimetype', file.mimetype);
28046         }
28047         
28048         if(this.fireEvent('prepare', this, formData) != false){
28049             this.xhr.send(formData);
28050         };
28051     }
28052 });
28053
28054 /*
28055 * Licence: LGPL
28056 */
28057
28058 /**
28059  * @class Roo.bootstrap.DocumentViewer
28060  * @extends Roo.bootstrap.Component
28061  * Bootstrap DocumentViewer class
28062  * 
28063  * @constructor
28064  * Create a new DocumentViewer
28065  * @param {Object} config The config object
28066  */
28067
28068 Roo.bootstrap.DocumentViewer = function(config){
28069     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28070     
28071     this.addEvents({
28072         /**
28073          * @event initial
28074          * Fire after initEvent
28075          * @param {Roo.bootstrap.DocumentViewer} this
28076          */
28077         "initial" : true,
28078         /**
28079          * @event click
28080          * Fire after click
28081          * @param {Roo.bootstrap.DocumentViewer} this
28082          */
28083         "click" : true,
28084         /**
28085          * @event trash
28086          * Fire after trash button
28087          * @param {Roo.bootstrap.DocumentViewer} this
28088          */
28089         "trash" : true
28090         
28091     });
28092 };
28093
28094 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28095     
28096     getAutoCreate : function()
28097     {
28098         var cfg = {
28099             tag : 'div',
28100             cls : 'roo-document-viewer',
28101             cn : [
28102                 {
28103                     tag : 'div',
28104                     cls : 'roo-document-viewer-body',
28105                     cn : [
28106                         {
28107                             tag : 'div',
28108                             cls : 'roo-document-viewer-thumb',
28109                             cn : [
28110                                 {
28111                                     tag : 'img',
28112                                     cls : 'roo-document-viewer-image'
28113                                 }
28114                             ]
28115                         }
28116                     ]
28117                 },
28118                 {
28119                     tag : 'div',
28120                     cls : 'roo-document-viewer-footer',
28121                     cn : {
28122                         tag : 'div',
28123                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28124                         cn : [
28125                             {
28126                                 tag : 'div',
28127                                 cls : 'btn-group',
28128                                 cn : [
28129                                     {
28130                                         tag : 'button',
28131                                         cls : 'btn btn-default roo-document-viewer-trash',
28132                                         html : '<i class="fa fa-trash"></i>'
28133                                     }
28134                                 ]
28135                             }
28136                         ]
28137                     }
28138                 }
28139             ]
28140         };
28141         
28142         return cfg;
28143     },
28144     
28145     initEvents : function()
28146     {
28147         
28148         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28149         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28150         
28151         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28152         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28153         
28154         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28155         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28156         
28157         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28158         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28159         
28160         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28161         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28162         
28163         this.bodyEl.on('click', this.onClick, this);
28164         
28165         this.trashBtn.on('click', this.onTrash, this);
28166         
28167     },
28168     
28169     initial : function()
28170     {
28171 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28172         
28173         
28174         this.fireEvent('initial', this);
28175         
28176     },
28177     
28178     onClick : function(e)
28179     {
28180         e.preventDefault();
28181         
28182         this.fireEvent('click', this);
28183     },
28184     
28185     onTrash : function(e)
28186     {
28187         e.preventDefault();
28188         
28189         this.fireEvent('trash', this);
28190     }
28191     
28192 });
28193 /*
28194  * - LGPL
28195  *
28196  * nav progress bar
28197  * 
28198  */
28199
28200 /**
28201  * @class Roo.bootstrap.NavProgressBar
28202  * @extends Roo.bootstrap.Component
28203  * Bootstrap NavProgressBar class
28204  * 
28205  * @constructor
28206  * Create a new nav progress bar
28207  * @param {Object} config The config object
28208  */
28209
28210 Roo.bootstrap.NavProgressBar = function(config){
28211     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28212
28213     this.bullets = this.bullets || [];
28214    
28215 //    Roo.bootstrap.NavProgressBar.register(this);
28216      this.addEvents({
28217         /**
28218              * @event changed
28219              * Fires when the active item changes
28220              * @param {Roo.bootstrap.NavProgressBar} this
28221              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28222              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28223          */
28224         'changed': true
28225      });
28226     
28227 };
28228
28229 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28230     
28231     bullets : [],
28232     barItems : [],
28233     
28234     getAutoCreate : function()
28235     {
28236         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28237         
28238         cfg = {
28239             tag : 'div',
28240             cls : 'roo-navigation-bar-group',
28241             cn : [
28242                 {
28243                     tag : 'div',
28244                     cls : 'roo-navigation-top-bar'
28245                 },
28246                 {
28247                     tag : 'div',
28248                     cls : 'roo-navigation-bullets-bar',
28249                     cn : [
28250                         {
28251                             tag : 'ul',
28252                             cls : 'roo-navigation-bar'
28253                         }
28254                     ]
28255                 },
28256                 
28257                 {
28258                     tag : 'div',
28259                     cls : 'roo-navigation-bottom-bar'
28260                 }
28261             ]
28262             
28263         };
28264         
28265         return cfg;
28266         
28267     },
28268     
28269     initEvents: function() 
28270     {
28271         
28272     },
28273     
28274     onRender : function(ct, position) 
28275     {
28276         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28277         
28278         if(this.bullets.length){
28279             Roo.each(this.bullets, function(b){
28280                this.addItem(b);
28281             }, this);
28282         }
28283         
28284         this.format();
28285         
28286     },
28287     
28288     addItem : function(cfg)
28289     {
28290         var item = new Roo.bootstrap.NavProgressItem(cfg);
28291         
28292         item.parentId = this.id;
28293         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28294         
28295         if(cfg.html){
28296             var top = new Roo.bootstrap.Element({
28297                 tag : 'div',
28298                 cls : 'roo-navigation-bar-text'
28299             });
28300             
28301             var bottom = new Roo.bootstrap.Element({
28302                 tag : 'div',
28303                 cls : 'roo-navigation-bar-text'
28304             });
28305             
28306             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28307             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28308             
28309             var topText = new Roo.bootstrap.Element({
28310                 tag : 'span',
28311                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28312             });
28313             
28314             var bottomText = new Roo.bootstrap.Element({
28315                 tag : 'span',
28316                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28317             });
28318             
28319             topText.onRender(top.el, null);
28320             bottomText.onRender(bottom.el, null);
28321             
28322             item.topEl = top;
28323             item.bottomEl = bottom;
28324         }
28325         
28326         this.barItems.push(item);
28327         
28328         return item;
28329     },
28330     
28331     getActive : function()
28332     {
28333         var active = false;
28334         
28335         Roo.each(this.barItems, function(v){
28336             
28337             if (!v.isActive()) {
28338                 return;
28339             }
28340             
28341             active = v;
28342             return false;
28343             
28344         });
28345         
28346         return active;
28347     },
28348     
28349     setActiveItem : function(item)
28350     {
28351         var prev = false;
28352         
28353         Roo.each(this.barItems, function(v){
28354             if (v.rid == item.rid) {
28355                 return ;
28356             }
28357             
28358             if (v.isActive()) {
28359                 v.setActive(false);
28360                 prev = v;
28361             }
28362         });
28363
28364         item.setActive(true);
28365         
28366         this.fireEvent('changed', this, item, prev);
28367     },
28368     
28369     getBarItem: function(rid)
28370     {
28371         var ret = false;
28372         
28373         Roo.each(this.barItems, function(e) {
28374             if (e.rid != rid) {
28375                 return;
28376             }
28377             
28378             ret =  e;
28379             return false;
28380         });
28381         
28382         return ret;
28383     },
28384     
28385     indexOfItem : function(item)
28386     {
28387         var index = false;
28388         
28389         Roo.each(this.barItems, function(v, i){
28390             
28391             if (v.rid != item.rid) {
28392                 return;
28393             }
28394             
28395             index = i;
28396             return false
28397         });
28398         
28399         return index;
28400     },
28401     
28402     setActiveNext : function()
28403     {
28404         var i = this.indexOfItem(this.getActive());
28405         
28406         if (i > this.barItems.length) {
28407             return;
28408         }
28409         
28410         this.setActiveItem(this.barItems[i+1]);
28411     },
28412     
28413     setActivePrev : function()
28414     {
28415         var i = this.indexOfItem(this.getActive());
28416         
28417         if (i  < 1) {
28418             return;
28419         }
28420         
28421         this.setActiveItem(this.barItems[i-1]);
28422     },
28423     
28424     format : function()
28425     {
28426         if(!this.barItems.length){
28427             return;
28428         }
28429      
28430         var width = 100 / this.barItems.length;
28431         
28432         Roo.each(this.barItems, function(i){
28433             i.el.setStyle('width', width + '%');
28434             i.topEl.el.setStyle('width', width + '%');
28435             i.bottomEl.el.setStyle('width', width + '%');
28436         }, this);
28437         
28438     }
28439     
28440 });
28441 /*
28442  * - LGPL
28443  *
28444  * Nav Progress Item
28445  * 
28446  */
28447
28448 /**
28449  * @class Roo.bootstrap.NavProgressItem
28450  * @extends Roo.bootstrap.Component
28451  * Bootstrap NavProgressItem class
28452  * @cfg {String} rid the reference id
28453  * @cfg {Boolean} active (true|false) Is item active default false
28454  * @cfg {Boolean} disabled (true|false) Is item active default false
28455  * @cfg {String} html
28456  * @cfg {String} position (top|bottom) text position default bottom
28457  * @cfg {String} icon show icon instead of number
28458  * 
28459  * @constructor
28460  * Create a new NavProgressItem
28461  * @param {Object} config The config object
28462  */
28463 Roo.bootstrap.NavProgressItem = function(config){
28464     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28465     this.addEvents({
28466         // raw events
28467         /**
28468          * @event click
28469          * The raw click event for the entire grid.
28470          * @param {Roo.bootstrap.NavProgressItem} this
28471          * @param {Roo.EventObject} e
28472          */
28473         "click" : true
28474     });
28475    
28476 };
28477
28478 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28479     
28480     rid : '',
28481     active : false,
28482     disabled : false,
28483     html : '',
28484     position : 'bottom',
28485     icon : false,
28486     
28487     getAutoCreate : function()
28488     {
28489         var iconCls = 'roo-navigation-bar-item-icon';
28490         
28491         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28492         
28493         var cfg = {
28494             tag: 'li',
28495             cls: 'roo-navigation-bar-item',
28496             cn : [
28497                 {
28498                     tag : 'i',
28499                     cls : iconCls
28500                 }
28501             ]
28502         };
28503         
28504         if(this.active){
28505             cfg.cls += ' active';
28506         }
28507         if(this.disabled){
28508             cfg.cls += ' disabled';
28509         }
28510         
28511         return cfg;
28512     },
28513     
28514     disable : function()
28515     {
28516         this.setDisabled(true);
28517     },
28518     
28519     enable : function()
28520     {
28521         this.setDisabled(false);
28522     },
28523     
28524     initEvents: function() 
28525     {
28526         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28527         
28528         this.iconEl.on('click', this.onClick, this);
28529     },
28530     
28531     onClick : function(e)
28532     {
28533         e.preventDefault();
28534         
28535         if(this.disabled){
28536             return;
28537         }
28538         
28539         if(this.fireEvent('click', this, e) === false){
28540             return;
28541         };
28542         
28543         this.parent().setActiveItem(this);
28544     },
28545     
28546     isActive: function () 
28547     {
28548         return this.active;
28549     },
28550     
28551     setActive : function(state)
28552     {
28553         if(this.active == state){
28554             return;
28555         }
28556         
28557         this.active = state;
28558         
28559         if (state) {
28560             this.el.addClass('active');
28561             return;
28562         }
28563         
28564         this.el.removeClass('active');
28565         
28566         return;
28567     },
28568     
28569     setDisabled : function(state)
28570     {
28571         if(this.disabled == state){
28572             return;
28573         }
28574         
28575         this.disabled = state;
28576         
28577         if (state) {
28578             this.el.addClass('disabled');
28579             return;
28580         }
28581         
28582         this.el.removeClass('disabled');
28583     },
28584     
28585     tooltipEl : function()
28586     {
28587         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28588     }
28589 });
28590  
28591
28592  /*
28593  * - LGPL
28594  *
28595  * FieldLabel
28596  * 
28597  */
28598
28599 /**
28600  * @class Roo.bootstrap.FieldLabel
28601  * @extends Roo.bootstrap.Component
28602  * Bootstrap FieldLabel class
28603  * @cfg {String} html contents of the element
28604  * @cfg {String} tag tag of the element default label
28605  * @cfg {String} cls class of the element
28606  * @cfg {String} target label target 
28607  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28608  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28609  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28610  * @cfg {String} iconTooltip default "This field is required"
28611  * 
28612  * @constructor
28613  * Create a new FieldLabel
28614  * @param {Object} config The config object
28615  */
28616
28617 Roo.bootstrap.FieldLabel = function(config){
28618     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28619     
28620     this.addEvents({
28621             /**
28622              * @event invalid
28623              * Fires after the field has been marked as invalid.
28624              * @param {Roo.form.FieldLabel} this
28625              * @param {String} msg The validation message
28626              */
28627             invalid : true,
28628             /**
28629              * @event valid
28630              * Fires after the field has been validated with no errors.
28631              * @param {Roo.form.FieldLabel} this
28632              */
28633             valid : true
28634         });
28635 };
28636
28637 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28638     
28639     tag: 'label',
28640     cls: '',
28641     html: '',
28642     target: '',
28643     allowBlank : true,
28644     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28645     validClass : 'text-success fa fa-lg fa-check',
28646     iconTooltip : 'This field is required',
28647     
28648     getAutoCreate : function(){
28649         
28650         var cfg = {
28651             tag : this.tag,
28652             cls : 'roo-bootstrap-field-label ' + this.cls,
28653             for : this.target,
28654             cn : [
28655                 {
28656                     tag : 'i',
28657                     cls : '',
28658                     tooltip : this.iconTooltip
28659                 },
28660                 {
28661                     tag : 'span',
28662                     html : this.html
28663                 }
28664             ] 
28665         };
28666         
28667         return cfg;
28668     },
28669     
28670     initEvents: function() 
28671     {
28672         Roo.bootstrap.Element.superclass.initEvents.call(this);
28673         
28674         this.iconEl = this.el.select('i', true).first();
28675         
28676         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28677         
28678         Roo.bootstrap.FieldLabel.register(this);
28679     },
28680     
28681     /**
28682      * Mark this field as valid
28683      */
28684     markValid : function()
28685     {
28686         this.iconEl.show();
28687         
28688         this.iconEl.removeClass(this.invalidClass);
28689         
28690         this.iconEl.addClass(this.validClass);
28691         
28692         this.fireEvent('valid', this);
28693     },
28694     
28695     /**
28696      * Mark this field as invalid
28697      * @param {String} msg The validation message
28698      */
28699     markInvalid : function(msg)
28700     {
28701         this.iconEl.show();
28702         
28703         this.iconEl.removeClass(this.validClass);
28704         
28705         this.iconEl.addClass(this.invalidClass);
28706         
28707         this.fireEvent('invalid', this, msg);
28708     }
28709     
28710    
28711 });
28712
28713 Roo.apply(Roo.bootstrap.FieldLabel, {
28714     
28715     groups: {},
28716     
28717      /**
28718     * register a FieldLabel Group
28719     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28720     */
28721     register : function(label)
28722     {
28723         if(this.groups.hasOwnProperty(label.target)){
28724             return;
28725         }
28726      
28727         this.groups[label.target] = label;
28728         
28729     },
28730     /**
28731     * fetch a FieldLabel Group based on the target
28732     * @param {string} target
28733     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28734     */
28735     get: function(target) {
28736         if (typeof(this.groups[target]) == 'undefined') {
28737             return false;
28738         }
28739         
28740         return this.groups[target] ;
28741     }
28742 });
28743
28744  
28745
28746  /*
28747  * - LGPL
28748  *
28749  * page DateSplitField.
28750  * 
28751  */
28752
28753
28754 /**
28755  * @class Roo.bootstrap.DateSplitField
28756  * @extends Roo.bootstrap.Component
28757  * Bootstrap DateSplitField class
28758  * @cfg {string} fieldLabel - the label associated
28759  * @cfg {Number} labelWidth set the width of label (0-12)
28760  * @cfg {String} labelAlign (top|left)
28761  * @cfg {Boolean} dayAllowBlank (true|false) default false
28762  * @cfg {Boolean} monthAllowBlank (true|false) default false
28763  * @cfg {Boolean} yearAllowBlank (true|false) default false
28764  * @cfg {string} dayPlaceholder 
28765  * @cfg {string} monthPlaceholder
28766  * @cfg {string} yearPlaceholder
28767  * @cfg {string} dayFormat default 'd'
28768  * @cfg {string} monthFormat default 'm'
28769  * @cfg {string} yearFormat default 'Y'
28770
28771  *     
28772  * @constructor
28773  * Create a new DateSplitField
28774  * @param {Object} config The config object
28775  */
28776
28777 Roo.bootstrap.DateSplitField = function(config){
28778     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28779     
28780     this.addEvents({
28781         // raw events
28782          /**
28783          * @event years
28784          * getting the data of years
28785          * @param {Roo.bootstrap.DateSplitField} this
28786          * @param {Object} years
28787          */
28788         "years" : true,
28789         /**
28790          * @event days
28791          * getting the data of days
28792          * @param {Roo.bootstrap.DateSplitField} this
28793          * @param {Object} days
28794          */
28795         "days" : true,
28796         /**
28797          * @event invalid
28798          * Fires after the field has been marked as invalid.
28799          * @param {Roo.form.Field} this
28800          * @param {String} msg The validation message
28801          */
28802         invalid : true,
28803        /**
28804          * @event valid
28805          * Fires after the field has been validated with no errors.
28806          * @param {Roo.form.Field} this
28807          */
28808         valid : true
28809     });
28810 };
28811
28812 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28813     
28814     fieldLabel : '',
28815     labelAlign : 'top',
28816     labelWidth : 3,
28817     dayAllowBlank : false,
28818     monthAllowBlank : false,
28819     yearAllowBlank : false,
28820     dayPlaceholder : '',
28821     monthPlaceholder : '',
28822     yearPlaceholder : '',
28823     dayFormat : 'd',
28824     monthFormat : 'm',
28825     yearFormat : 'Y',
28826     isFormField : true,
28827     
28828     getAutoCreate : function()
28829     {
28830         var cfg = {
28831             tag : 'div',
28832             cls : 'row roo-date-split-field-group',
28833             cn : [
28834                 {
28835                     tag : 'input',
28836                     type : 'hidden',
28837                     cls : 'form-hidden-field roo-date-split-field-group-value',
28838                     name : this.name
28839                 }
28840             ]
28841         };
28842         
28843         if(this.fieldLabel){
28844             cfg.cn.push({
28845                 tag : 'div',
28846                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28847                 cn : [
28848                     {
28849                         tag : 'label',
28850                         html : this.fieldLabel
28851                     }
28852                 ]
28853             });
28854         }
28855         
28856         Roo.each(['day', 'month', 'year'], function(t){
28857             cfg.cn.push({
28858                 tag : 'div',
28859                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28860             });
28861         }, this);
28862         
28863         return cfg;
28864     },
28865     
28866     inputEl: function ()
28867     {
28868         return this.el.select('.roo-date-split-field-group-value', true).first();
28869     },
28870     
28871     onRender : function(ct, position) 
28872     {
28873         var _this = this;
28874         
28875         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28876         
28877         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28878         
28879         this.dayField = new Roo.bootstrap.ComboBox({
28880             allowBlank : this.dayAllowBlank,
28881             alwaysQuery : true,
28882             displayField : 'value',
28883             editable : false,
28884             fieldLabel : '',
28885             forceSelection : true,
28886             mode : 'local',
28887             placeholder : this.dayPlaceholder,
28888             selectOnFocus : true,
28889             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28890             triggerAction : 'all',
28891             typeAhead : true,
28892             valueField : 'value',
28893             store : new Roo.data.SimpleStore({
28894                 data : (function() {    
28895                     var days = [];
28896                     _this.fireEvent('days', _this, days);
28897                     return days;
28898                 })(),
28899                 fields : [ 'value' ]
28900             }),
28901             listeners : {
28902                 select : function (_self, record, index)
28903                 {
28904                     _this.setValue(_this.getValue());
28905                 }
28906             }
28907         });
28908
28909         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28910         
28911         this.monthField = new Roo.bootstrap.MonthField({
28912             after : '<i class=\"fa fa-calendar\"></i>',
28913             allowBlank : this.monthAllowBlank,
28914             placeholder : this.monthPlaceholder,
28915             readOnly : true,
28916             listeners : {
28917                 render : function (_self)
28918                 {
28919                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28920                         e.preventDefault();
28921                         _self.focus();
28922                     });
28923                 },
28924                 select : function (_self, oldvalue, newvalue)
28925                 {
28926                     _this.setValue(_this.getValue());
28927                 }
28928             }
28929         });
28930         
28931         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28932         
28933         this.yearField = new Roo.bootstrap.ComboBox({
28934             allowBlank : this.yearAllowBlank,
28935             alwaysQuery : true,
28936             displayField : 'value',
28937             editable : false,
28938             fieldLabel : '',
28939             forceSelection : true,
28940             mode : 'local',
28941             placeholder : this.yearPlaceholder,
28942             selectOnFocus : true,
28943             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28944             triggerAction : 'all',
28945             typeAhead : true,
28946             valueField : 'value',
28947             store : new Roo.data.SimpleStore({
28948                 data : (function() {
28949                     var years = [];
28950                     _this.fireEvent('years', _this, years);
28951                     return years;
28952                 })(),
28953                 fields : [ 'value' ]
28954             }),
28955             listeners : {
28956                 select : function (_self, record, index)
28957                 {
28958                     _this.setValue(_this.getValue());
28959                 }
28960             }
28961         });
28962
28963         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28964     },
28965     
28966     setValue : function(v, format)
28967     {
28968         this.inputEl.dom.value = v;
28969         
28970         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28971         
28972         var d = Date.parseDate(v, f);
28973         
28974         if(!d){
28975             this.validate();
28976             return;
28977         }
28978         
28979         this.setDay(d.format(this.dayFormat));
28980         this.setMonth(d.format(this.monthFormat));
28981         this.setYear(d.format(this.yearFormat));
28982         
28983         this.validate();
28984         
28985         return;
28986     },
28987     
28988     setDay : function(v)
28989     {
28990         this.dayField.setValue(v);
28991         this.inputEl.dom.value = this.getValue();
28992         this.validate();
28993         return;
28994     },
28995     
28996     setMonth : function(v)
28997     {
28998         this.monthField.setValue(v, true);
28999         this.inputEl.dom.value = this.getValue();
29000         this.validate();
29001         return;
29002     },
29003     
29004     setYear : function(v)
29005     {
29006         this.yearField.setValue(v);
29007         this.inputEl.dom.value = this.getValue();
29008         this.validate();
29009         return;
29010     },
29011     
29012     getDay : function()
29013     {
29014         return this.dayField.getValue();
29015     },
29016     
29017     getMonth : function()
29018     {
29019         return this.monthField.getValue();
29020     },
29021     
29022     getYear : function()
29023     {
29024         return this.yearField.getValue();
29025     },
29026     
29027     getValue : function()
29028     {
29029         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29030         
29031         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29032         
29033         return date;
29034     },
29035     
29036     reset : function()
29037     {
29038         this.setDay('');
29039         this.setMonth('');
29040         this.setYear('');
29041         this.inputEl.dom.value = '';
29042         this.validate();
29043         return;
29044     },
29045     
29046     validate : function()
29047     {
29048         var d = this.dayField.validate();
29049         var m = this.monthField.validate();
29050         var y = this.yearField.validate();
29051         
29052         var valid = true;
29053         
29054         if(
29055                 (!this.dayAllowBlank && !d) ||
29056                 (!this.monthAllowBlank && !m) ||
29057                 (!this.yearAllowBlank && !y)
29058         ){
29059             valid = false;
29060         }
29061         
29062         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29063             return valid;
29064         }
29065         
29066         if(valid){
29067             this.markValid();
29068             return valid;
29069         }
29070         
29071         this.markInvalid();
29072         
29073         return valid;
29074     },
29075     
29076     markValid : function()
29077     {
29078         
29079         var label = this.el.select('label', true).first();
29080         var icon = this.el.select('i.fa-star', true).first();
29081
29082         if(label && icon){
29083             icon.remove();
29084         }
29085         
29086         this.fireEvent('valid', this);
29087     },
29088     
29089      /**
29090      * Mark this field as invalid
29091      * @param {String} msg The validation message
29092      */
29093     markInvalid : function(msg)
29094     {
29095         
29096         var label = this.el.select('label', true).first();
29097         var icon = this.el.select('i.fa-star', true).first();
29098
29099         if(label && !icon){
29100             this.el.select('.roo-date-split-field-label', true).createChild({
29101                 tag : 'i',
29102                 cls : 'text-danger fa fa-lg fa-star',
29103                 tooltip : 'This field is required',
29104                 style : 'margin-right:5px;'
29105             }, label, true);
29106         }
29107         
29108         this.fireEvent('invalid', this, msg);
29109     },
29110     
29111     clearInvalid : function()
29112     {
29113         var label = this.el.select('label', true).first();
29114         var icon = this.el.select('i.fa-star', true).first();
29115
29116         if(label && icon){
29117             icon.remove();
29118         }
29119         
29120         this.fireEvent('valid', this);
29121     },
29122     
29123     getName: function()
29124     {
29125         return this.name;
29126     }
29127     
29128 });
29129
29130  /**
29131  *
29132  * This is based on 
29133  * http://masonry.desandro.com
29134  *
29135  * The idea is to render all the bricks based on vertical width...
29136  *
29137  * The original code extends 'outlayer' - we might need to use that....
29138  * 
29139  */
29140
29141
29142 /**
29143  * @class Roo.bootstrap.LayoutMasonry
29144  * @extends Roo.bootstrap.Component
29145  * Bootstrap Layout Masonry class
29146  * 
29147  * @constructor
29148  * Create a new Element
29149  * @param {Object} config The config object
29150  */
29151
29152 Roo.bootstrap.LayoutMasonry = function(config){
29153     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29154     
29155     this.bricks = [];
29156     
29157 };
29158
29159 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29160     
29161     /**
29162      * @cfg {Boolean} isLayoutInstant = no animation?
29163      */   
29164     isLayoutInstant : false, // needed?
29165    
29166     /**
29167      * @cfg {Number} boxWidth  width of the columns
29168      */   
29169     boxWidth : 450,
29170     
29171       /**
29172      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29173      */   
29174     boxHeight : 0,
29175     
29176     /**
29177      * @cfg {Number} padWidth padding below box..
29178      */   
29179     padWidth : 10, 
29180     
29181     /**
29182      * @cfg {Number} gutter gutter width..
29183      */   
29184     gutter : 10,
29185     
29186      /**
29187      * @cfg {Number} maxCols maximum number of columns
29188      */   
29189     
29190     maxCols: 0,
29191     
29192     /**
29193      * @cfg {Boolean} isAutoInitial defalut true
29194      */   
29195     isAutoInitial : true, 
29196     
29197     containerWidth: 0,
29198     
29199     /**
29200      * @cfg {Boolean} isHorizontal defalut false
29201      */   
29202     isHorizontal : false, 
29203
29204     currentSize : null,
29205     
29206     tag: 'div',
29207     
29208     cls: '',
29209     
29210     bricks: null, //CompositeElement
29211     
29212     cols : 1,
29213     
29214     _isLayoutInited : false,
29215     
29216 //    isAlternative : false, // only use for vertical layout...
29217     
29218     /**
29219      * @cfg {Number} alternativePadWidth padding below box..
29220      */   
29221     alternativePadWidth : 50, 
29222     
29223     getAutoCreate : function(){
29224         
29225         var cfg = {
29226             tag: this.tag,
29227             cls: 'blog-masonary-wrapper ' + this.cls,
29228             cn : {
29229                 cls : 'mas-boxes masonary'
29230             }
29231         };
29232         
29233         return cfg;
29234     },
29235     
29236     getChildContainer: function( )
29237     {
29238         if (this.boxesEl) {
29239             return this.boxesEl;
29240         }
29241         
29242         this.boxesEl = this.el.select('.mas-boxes').first();
29243         
29244         return this.boxesEl;
29245     },
29246     
29247     
29248     initEvents : function()
29249     {
29250         var _this = this;
29251         
29252         if(this.isAutoInitial){
29253             Roo.log('hook children rendered');
29254             this.on('childrenrendered', function() {
29255                 Roo.log('children rendered');
29256                 _this.initial();
29257             } ,this);
29258         }
29259     },
29260     
29261     initial : function()
29262     {
29263         this.currentSize = this.el.getBox(true);
29264         
29265         Roo.EventManager.onWindowResize(this.resize, this); 
29266
29267         if(!this.isAutoInitial){
29268             this.layout();
29269             return;
29270         }
29271         
29272         this.layout();
29273         
29274         return;
29275         //this.layout.defer(500,this);
29276         
29277     },
29278     
29279     resize : function()
29280     {
29281         Roo.log('resize');
29282         
29283         var cs = this.el.getBox(true);
29284         
29285         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29286             Roo.log("no change in with or X");
29287             return;
29288         }
29289         
29290         this.currentSize = cs;
29291         
29292         this.layout();
29293         
29294     },
29295     
29296     layout : function()
29297     {   
29298         this._resetLayout();
29299         
29300         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29301         
29302         this.layoutItems( isInstant );
29303       
29304         this._isLayoutInited = true;
29305         
29306     },
29307     
29308     _resetLayout : function()
29309     {
29310         if(this.isHorizontal){
29311             this.horizontalMeasureColumns();
29312             return;
29313         }
29314         
29315         this.verticalMeasureColumns();
29316         
29317     },
29318     
29319     verticalMeasureColumns : function()
29320     {
29321         this.getContainerWidth();
29322         
29323 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29324 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29325 //            return;
29326 //        }
29327         
29328         var boxWidth = this.boxWidth + this.padWidth;
29329         
29330         if(this.containerWidth < this.boxWidth){
29331             boxWidth = this.containerWidth
29332         }
29333         
29334         var containerWidth = this.containerWidth;
29335         
29336         var cols = Math.floor(containerWidth / boxWidth);
29337         
29338         this.cols = Math.max( cols, 1 );
29339         
29340         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29341         
29342         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29343         
29344         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29345         
29346         this.colWidth = boxWidth + avail - this.padWidth;
29347         
29348         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29349         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29350     },
29351     
29352     horizontalMeasureColumns : function()
29353     {
29354         this.getContainerWidth();
29355         
29356         var boxWidth = this.boxWidth;
29357         
29358         if(this.containerWidth < boxWidth){
29359             boxWidth = this.containerWidth;
29360         }
29361         
29362         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29363         
29364         this.el.setHeight(boxWidth);
29365         
29366     },
29367     
29368     getContainerWidth : function()
29369     {
29370         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29371     },
29372     
29373     layoutItems : function( isInstant )
29374     {
29375         var items = Roo.apply([], this.bricks);
29376         
29377         if(this.isHorizontal){
29378             this._horizontalLayoutItems( items , isInstant );
29379             return;
29380         }
29381         
29382 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29383 //            this._verticalAlternativeLayoutItems( items , isInstant );
29384 //            return;
29385 //        }
29386         
29387         this._verticalLayoutItems( items , isInstant );
29388         
29389     },
29390     
29391     _verticalLayoutItems : function ( items , isInstant)
29392     {
29393         if ( !items || !items.length ) {
29394             return;
29395         }
29396         
29397         var standard = [
29398             ['xs', 'xs', 'xs', 'tall'],
29399             ['xs', 'xs', 'tall'],
29400             ['xs', 'xs', 'sm'],
29401             ['xs', 'xs', 'xs'],
29402             ['xs', 'tall'],
29403             ['xs', 'sm'],
29404             ['xs', 'xs'],
29405             ['xs'],
29406             
29407             ['sm', 'xs', 'xs'],
29408             ['sm', 'xs'],
29409             ['sm'],
29410             
29411             ['tall', 'xs', 'xs', 'xs'],
29412             ['tall', 'xs', 'xs'],
29413             ['tall', 'xs'],
29414             ['tall']
29415             
29416         ];
29417         
29418         var queue = [];
29419         
29420         var boxes = [];
29421         
29422         var box = [];
29423         
29424         Roo.each(items, function(item, k){
29425             
29426             switch (item.size) {
29427                 // these layouts take up a full box,
29428                 case 'md' :
29429                 case 'md-left' :
29430                 case 'md-right' :
29431                 case 'wide' :
29432                     
29433                     if(box.length){
29434                         boxes.push(box);
29435                         box = [];
29436                     }
29437                     
29438                     boxes.push([item]);
29439                     
29440                     break;
29441                     
29442                 case 'xs' :
29443                 case 'sm' :
29444                 case 'tall' :
29445                     
29446                     box.push(item);
29447                     
29448                     break;
29449                 default :
29450                     break;
29451                     
29452             }
29453             
29454         }, this);
29455         
29456         if(box.length){
29457             boxes.push(box);
29458             box = [];
29459         }
29460         
29461         var filterPattern = function(box, length)
29462         {
29463             if(!box.length){
29464                 return;
29465             }
29466             
29467             var match = false;
29468             
29469             var pattern = box.slice(0, length);
29470             
29471             var format = [];
29472             
29473             Roo.each(pattern, function(i){
29474                 format.push(i.size);
29475             }, this);
29476             
29477             Roo.each(standard, function(s){
29478                 
29479                 if(String(s) != String(format)){
29480                     return;
29481                 }
29482                 
29483                 match = true;
29484                 return false;
29485                 
29486             }, this);
29487             
29488             if(!match && length == 1){
29489                 return;
29490             }
29491             
29492             if(!match){
29493                 filterPattern(box, length - 1);
29494                 return;
29495             }
29496                 
29497             queue.push(pattern);
29498
29499             box = box.slice(length, box.length);
29500
29501             filterPattern(box, 4);
29502
29503             return;
29504             
29505         }
29506         
29507         Roo.each(boxes, function(box, k){
29508             
29509             if(!box.length){
29510                 return;
29511             }
29512             
29513             if(box.length == 1){
29514                 queue.push(box);
29515                 return;
29516             }
29517             
29518             filterPattern(box, 4);
29519             
29520         }, this);
29521         
29522         this._processVerticalLayoutQueue( queue, isInstant );
29523         
29524     },
29525     
29526 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29527 //    {
29528 //        if ( !items || !items.length ) {
29529 //            return;
29530 //        }
29531 //
29532 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29533 //        
29534 //    },
29535     
29536     _horizontalLayoutItems : function ( items , isInstant)
29537     {
29538         if ( !items || !items.length || items.length < 3) {
29539             return;
29540         }
29541         
29542         items.reverse();
29543         
29544         var eItems = items.slice(0, 3);
29545         
29546         items = items.slice(3, items.length);
29547         
29548         var standard = [
29549             ['xs', 'xs', 'xs', 'wide'],
29550             ['xs', 'xs', 'wide'],
29551             ['xs', 'xs', 'sm'],
29552             ['xs', 'xs', 'xs'],
29553             ['xs', 'wide'],
29554             ['xs', 'sm'],
29555             ['xs', 'xs'],
29556             ['xs'],
29557             
29558             ['sm', 'xs', 'xs'],
29559             ['sm', 'xs'],
29560             ['sm'],
29561             
29562             ['wide', 'xs', 'xs', 'xs'],
29563             ['wide', 'xs', 'xs'],
29564             ['wide', 'xs'],
29565             ['wide'],
29566             
29567             ['wide-thin']
29568         ];
29569         
29570         var queue = [];
29571         
29572         var boxes = [];
29573         
29574         var box = [];
29575         
29576         Roo.each(items, function(item, k){
29577             
29578             switch (item.size) {
29579                 case 'md' :
29580                 case 'md-left' :
29581                 case 'md-right' :
29582                 case 'tall' :
29583                     
29584                     if(box.length){
29585                         boxes.push(box);
29586                         box = [];
29587                     }
29588                     
29589                     boxes.push([item]);
29590                     
29591                     break;
29592                     
29593                 case 'xs' :
29594                 case 'sm' :
29595                 case 'wide' :
29596                 case 'wide-thin' :
29597                     
29598                     box.push(item);
29599                     
29600                     break;
29601                 default :
29602                     break;
29603                     
29604             }
29605             
29606         }, this);
29607         
29608         if(box.length){
29609             boxes.push(box);
29610             box = [];
29611         }
29612         
29613         var filterPattern = function(box, length)
29614         {
29615             if(!box.length){
29616                 return;
29617             }
29618             
29619             var match = false;
29620             
29621             var pattern = box.slice(0, length);
29622             
29623             var format = [];
29624             
29625             Roo.each(pattern, function(i){
29626                 format.push(i.size);
29627             }, this);
29628             
29629             Roo.each(standard, function(s){
29630                 
29631                 if(String(s) != String(format)){
29632                     return;
29633                 }
29634                 
29635                 match = true;
29636                 return false;
29637                 
29638             }, this);
29639             
29640             if(!match && length == 1){
29641                 return;
29642             }
29643             
29644             if(!match){
29645                 filterPattern(box, length - 1);
29646                 return;
29647             }
29648                 
29649             queue.push(pattern);
29650
29651             box = box.slice(length, box.length);
29652
29653             filterPattern(box, 4);
29654
29655             return;
29656             
29657         }
29658         
29659         Roo.each(boxes, function(box, k){
29660             
29661             if(!box.length){
29662                 return;
29663             }
29664             
29665             if(box.length == 1){
29666                 queue.push(box);
29667                 return;
29668             }
29669             
29670             filterPattern(box, 4);
29671             
29672         }, this);
29673         
29674         
29675         var prune = [];
29676         
29677         var pos = this.el.getBox(true);
29678         
29679         var minX = pos.x;
29680         
29681         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29682         
29683         var hit_end = false;
29684         
29685         Roo.each(queue, function(box){
29686             
29687             if(hit_end){
29688                 
29689                 Roo.each(box, function(b){
29690                 
29691                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29692                     b.el.hide();
29693
29694                 }, this);
29695
29696                 return;
29697             }
29698             
29699             var mx = 0;
29700             
29701             Roo.each(box, function(b){
29702                 
29703                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29704                 b.el.show();
29705
29706                 mx = Math.max(mx, b.x);
29707                 
29708             }, this);
29709             
29710             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29711             
29712             if(maxX < minX){
29713                 
29714                 Roo.each(box, function(b){
29715                 
29716                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29717                     b.el.hide();
29718                     
29719                 }, this);
29720                 
29721                 hit_end = true;
29722                 
29723                 return;
29724             }
29725             
29726             prune.push(box);
29727             
29728         }, this);
29729         
29730         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29731     },
29732     
29733     /** Sets position of item in DOM
29734     * @param {Element} item
29735     * @param {Number} x - horizontal position
29736     * @param {Number} y - vertical position
29737     * @param {Boolean} isInstant - disables transitions
29738     */
29739     _processVerticalLayoutQueue : function( queue, isInstant )
29740     {
29741         var pos = this.el.getBox(true);
29742         var x = pos.x;
29743         var y = pos.y;
29744         var maxY = [];
29745         
29746         for (var i = 0; i < this.cols; i++){
29747             maxY[i] = pos.y;
29748         }
29749         
29750         Roo.each(queue, function(box, k){
29751             
29752             var col = k % this.cols;
29753             
29754             Roo.each(box, function(b,kk){
29755                 
29756                 b.el.position('absolute');
29757                 
29758                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29759                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29760                 
29761                 if(b.size == 'md-left' || b.size == 'md-right'){
29762                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29763                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29764                 }
29765                 
29766                 b.el.setWidth(width);
29767                 b.el.setHeight(height);
29768                 // iframe?
29769                 b.el.select('iframe',true).setSize(width,height);
29770                 
29771             }, this);
29772             
29773             for (var i = 0; i < this.cols; i++){
29774                 
29775                 if(maxY[i] < maxY[col]){
29776                     col = i;
29777                     continue;
29778                 }
29779                 
29780                 col = Math.min(col, i);
29781                 
29782             }
29783             
29784             x = pos.x + col * (this.colWidth + this.padWidth);
29785             
29786             y = maxY[col];
29787             
29788             var positions = [];
29789             
29790             switch (box.length){
29791                 case 1 :
29792                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29793                     break;
29794                 case 2 :
29795                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29796                     break;
29797                 case 3 :
29798                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29799                     break;
29800                 case 4 :
29801                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29802                     break;
29803                 default :
29804                     break;
29805             }
29806             
29807             Roo.each(box, function(b,kk){
29808                 
29809                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29810                 
29811                 var sz = b.el.getSize();
29812                 
29813                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29814                 
29815             }, this);
29816             
29817         }, this);
29818         
29819         var mY = 0;
29820         
29821         for (var i = 0; i < this.cols; i++){
29822             mY = Math.max(mY, maxY[i]);
29823         }
29824         
29825         this.el.setHeight(mY - pos.y);
29826         
29827     },
29828     
29829 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29830 //    {
29831 //        var pos = this.el.getBox(true);
29832 //        var x = pos.x;
29833 //        var y = pos.y;
29834 //        var maxX = pos.right;
29835 //        
29836 //        var maxHeight = 0;
29837 //        
29838 //        Roo.each(items, function(item, k){
29839 //            
29840 //            var c = k % 2;
29841 //            
29842 //            item.el.position('absolute');
29843 //                
29844 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29845 //
29846 //            item.el.setWidth(width);
29847 //
29848 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29849 //
29850 //            item.el.setHeight(height);
29851 //            
29852 //            if(c == 0){
29853 //                item.el.setXY([x, y], isInstant ? false : true);
29854 //            } else {
29855 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29856 //            }
29857 //            
29858 //            y = y + height + this.alternativePadWidth;
29859 //            
29860 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29861 //            
29862 //        }, this);
29863 //        
29864 //        this.el.setHeight(maxHeight);
29865 //        
29866 //    },
29867     
29868     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29869     {
29870         var pos = this.el.getBox(true);
29871         
29872         var minX = pos.x;
29873         var minY = pos.y;
29874         
29875         var maxX = pos.right;
29876         
29877         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29878         
29879         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29880         
29881         Roo.each(queue, function(box, k){
29882             
29883             Roo.each(box, function(b, kk){
29884                 
29885                 b.el.position('absolute');
29886                 
29887                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29888                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29889                 
29890                 if(b.size == 'md-left' || b.size == 'md-right'){
29891                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29892                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29893                 }
29894                 
29895                 b.el.setWidth(width);
29896                 b.el.setHeight(height);
29897                 
29898             }, this);
29899             
29900             if(!box.length){
29901                 return;
29902             }
29903             
29904             var positions = [];
29905             
29906             switch (box.length){
29907                 case 1 :
29908                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29909                     break;
29910                 case 2 :
29911                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29912                     break;
29913                 case 3 :
29914                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29915                     break;
29916                 case 4 :
29917                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29918                     break;
29919                 default :
29920                     break;
29921             }
29922             
29923             Roo.each(box, function(b,kk){
29924                 
29925                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29926                 
29927                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29928                 
29929             }, this);
29930             
29931         }, this);
29932         
29933     },
29934     
29935     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29936     {
29937         Roo.each(eItems, function(b,k){
29938             
29939             b.size = (k == 0) ? 'sm' : 'xs';
29940             b.x = (k == 0) ? 2 : 1;
29941             b.y = (k == 0) ? 2 : 1;
29942             
29943             b.el.position('absolute');
29944             
29945             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29946                 
29947             b.el.setWidth(width);
29948             
29949             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29950             
29951             b.el.setHeight(height);
29952             
29953         }, this);
29954
29955         var positions = [];
29956         
29957         positions.push({
29958             x : maxX - this.unitWidth * 2 - this.gutter,
29959             y : minY
29960         });
29961         
29962         positions.push({
29963             x : maxX - this.unitWidth,
29964             y : minY + (this.unitWidth + this.gutter) * 2
29965         });
29966         
29967         positions.push({
29968             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29969             y : minY
29970         });
29971         
29972         Roo.each(eItems, function(b,k){
29973             
29974             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29975
29976         }, this);
29977         
29978     },
29979     
29980     getVerticalOneBoxColPositions : function(x, y, box)
29981     {
29982         var pos = [];
29983         
29984         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29985         
29986         if(box[0].size == 'md-left'){
29987             rand = 0;
29988         }
29989         
29990         if(box[0].size == 'md-right'){
29991             rand = 1;
29992         }
29993         
29994         pos.push({
29995             x : x + (this.unitWidth + this.gutter) * rand,
29996             y : y
29997         });
29998         
29999         return pos;
30000     },
30001     
30002     getVerticalTwoBoxColPositions : function(x, y, box)
30003     {
30004         var pos = [];
30005         
30006         if(box[0].size == 'xs'){
30007             
30008             pos.push({
30009                 x : x,
30010                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30011             });
30012
30013             pos.push({
30014                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30015                 y : y
30016             });
30017             
30018             return pos;
30019             
30020         }
30021         
30022         pos.push({
30023             x : x,
30024             y : y
30025         });
30026
30027         pos.push({
30028             x : x + (this.unitWidth + this.gutter) * 2,
30029             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30030         });
30031         
30032         return pos;
30033         
30034     },
30035     
30036     getVerticalThreeBoxColPositions : function(x, y, box)
30037     {
30038         var pos = [];
30039         
30040         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30041             
30042             pos.push({
30043                 x : x,
30044                 y : y
30045             });
30046
30047             pos.push({
30048                 x : x + (this.unitWidth + this.gutter) * 1,
30049                 y : y
30050             });
30051             
30052             pos.push({
30053                 x : x + (this.unitWidth + this.gutter) * 2,
30054                 y : y
30055             });
30056             
30057             return pos;
30058             
30059         }
30060         
30061         if(box[0].size == 'xs' && box[1].size == 'xs'){
30062             
30063             pos.push({
30064                 x : x,
30065                 y : y
30066             });
30067
30068             pos.push({
30069                 x : x,
30070                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30071             });
30072             
30073             pos.push({
30074                 x : x + (this.unitWidth + this.gutter) * 1,
30075                 y : y
30076             });
30077             
30078             return pos;
30079             
30080         }
30081         
30082         pos.push({
30083             x : x,
30084             y : y
30085         });
30086
30087         pos.push({
30088             x : x + (this.unitWidth + this.gutter) * 2,
30089             y : y
30090         });
30091
30092         pos.push({
30093             x : x + (this.unitWidth + this.gutter) * 2,
30094             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30095         });
30096             
30097         return pos;
30098         
30099     },
30100     
30101     getVerticalFourBoxColPositions : function(x, y, box)
30102     {
30103         var pos = [];
30104         
30105         if(box[0].size == 'xs'){
30106             
30107             pos.push({
30108                 x : x,
30109                 y : y
30110             });
30111
30112             pos.push({
30113                 x : x,
30114                 y : y + (this.unitHeight + this.gutter) * 1
30115             });
30116             
30117             pos.push({
30118                 x : x,
30119                 y : y + (this.unitHeight + this.gutter) * 2
30120             });
30121             
30122             pos.push({
30123                 x : x + (this.unitWidth + this.gutter) * 1,
30124                 y : y
30125             });
30126             
30127             return pos;
30128             
30129         }
30130         
30131         pos.push({
30132             x : x,
30133             y : y
30134         });
30135
30136         pos.push({
30137             x : x + (this.unitWidth + this.gutter) * 2,
30138             y : y
30139         });
30140
30141         pos.push({
30142             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30143             y : y + (this.unitHeight + this.gutter) * 1
30144         });
30145
30146         pos.push({
30147             x : x + (this.unitWidth + this.gutter) * 2,
30148             y : y + (this.unitWidth + this.gutter) * 2
30149         });
30150
30151         return pos;
30152         
30153     },
30154     
30155     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30156     {
30157         var pos = [];
30158         
30159         if(box[0].size == 'md-left'){
30160             pos.push({
30161                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30162                 y : minY
30163             });
30164             
30165             return pos;
30166         }
30167         
30168         if(box[0].size == 'md-right'){
30169             pos.push({
30170                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30171                 y : minY + (this.unitWidth + this.gutter) * 1
30172             });
30173             
30174             return pos;
30175         }
30176         
30177         var rand = Math.floor(Math.random() * (4 - box[0].y));
30178         
30179         pos.push({
30180             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30181             y : minY + (this.unitWidth + this.gutter) * rand
30182         });
30183         
30184         return pos;
30185         
30186     },
30187     
30188     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30189     {
30190         var pos = [];
30191         
30192         if(box[0].size == 'xs'){
30193             
30194             pos.push({
30195                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30196                 y : minY
30197             });
30198
30199             pos.push({
30200                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30201                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30202             });
30203             
30204             return pos;
30205             
30206         }
30207         
30208         pos.push({
30209             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30210             y : minY
30211         });
30212
30213         pos.push({
30214             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30215             y : minY + (this.unitWidth + this.gutter) * 2
30216         });
30217         
30218         return pos;
30219         
30220     },
30221     
30222     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30223     {
30224         var pos = [];
30225         
30226         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30227             
30228             pos.push({
30229                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30230                 y : minY
30231             });
30232
30233             pos.push({
30234                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30235                 y : minY + (this.unitWidth + this.gutter) * 1
30236             });
30237             
30238             pos.push({
30239                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30240                 y : minY + (this.unitWidth + this.gutter) * 2
30241             });
30242             
30243             return pos;
30244             
30245         }
30246         
30247         if(box[0].size == 'xs' && box[1].size == 'xs'){
30248             
30249             pos.push({
30250                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30251                 y : minY
30252             });
30253
30254             pos.push({
30255                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30256                 y : minY
30257             });
30258             
30259             pos.push({
30260                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30261                 y : minY + (this.unitWidth + this.gutter) * 1
30262             });
30263             
30264             return pos;
30265             
30266         }
30267         
30268         pos.push({
30269             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30270             y : minY
30271         });
30272
30273         pos.push({
30274             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30275             y : minY + (this.unitWidth + this.gutter) * 2
30276         });
30277
30278         pos.push({
30279             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30280             y : minY + (this.unitWidth + this.gutter) * 2
30281         });
30282             
30283         return pos;
30284         
30285     },
30286     
30287     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30288     {
30289         var pos = [];
30290         
30291         if(box[0].size == 'xs'){
30292             
30293             pos.push({
30294                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30295                 y : minY
30296             });
30297
30298             pos.push({
30299                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30300                 y : minY
30301             });
30302             
30303             pos.push({
30304                 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),
30305                 y : minY
30306             });
30307             
30308             pos.push({
30309                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30310                 y : minY + (this.unitWidth + this.gutter) * 1
30311             });
30312             
30313             return pos;
30314             
30315         }
30316         
30317         pos.push({
30318             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30319             y : minY
30320         });
30321         
30322         pos.push({
30323             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30324             y : minY + (this.unitWidth + this.gutter) * 2
30325         });
30326         
30327         pos.push({
30328             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30329             y : minY + (this.unitWidth + this.gutter) * 2
30330         });
30331         
30332         pos.push({
30333             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),
30334             y : minY + (this.unitWidth + this.gutter) * 2
30335         });
30336
30337         return pos;
30338         
30339     }
30340     
30341 });
30342
30343  
30344
30345  /**
30346  *
30347  * This is based on 
30348  * http://masonry.desandro.com
30349  *
30350  * The idea is to render all the bricks based on vertical width...
30351  *
30352  * The original code extends 'outlayer' - we might need to use that....
30353  * 
30354  */
30355
30356
30357 /**
30358  * @class Roo.bootstrap.LayoutMasonryAuto
30359  * @extends Roo.bootstrap.Component
30360  * Bootstrap Layout Masonry class
30361  * 
30362  * @constructor
30363  * Create a new Element
30364  * @param {Object} config The config object
30365  */
30366
30367 Roo.bootstrap.LayoutMasonryAuto = function(config){
30368     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30369 };
30370
30371 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30372     
30373       /**
30374      * @cfg {Boolean} isFitWidth  - resize the width..
30375      */   
30376     isFitWidth : false,  // options..
30377     /**
30378      * @cfg {Boolean} isOriginLeft = left align?
30379      */   
30380     isOriginLeft : true,
30381     /**
30382      * @cfg {Boolean} isOriginTop = top align?
30383      */   
30384     isOriginTop : false,
30385     /**
30386      * @cfg {Boolean} isLayoutInstant = no animation?
30387      */   
30388     isLayoutInstant : false, // needed?
30389     /**
30390      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30391      */   
30392     isResizingContainer : true,
30393     /**
30394      * @cfg {Number} columnWidth  width of the columns 
30395      */   
30396     
30397     columnWidth : 0,
30398     
30399     /**
30400      * @cfg {Number} maxCols maximum number of columns
30401      */   
30402     
30403     maxCols: 0,
30404     /**
30405      * @cfg {Number} padHeight padding below box..
30406      */   
30407     
30408     padHeight : 10, 
30409     
30410     /**
30411      * @cfg {Boolean} isAutoInitial defalut true
30412      */   
30413     
30414     isAutoInitial : true, 
30415     
30416     // private?
30417     gutter : 0,
30418     
30419     containerWidth: 0,
30420     initialColumnWidth : 0,
30421     currentSize : null,
30422     
30423     colYs : null, // array.
30424     maxY : 0,
30425     padWidth: 10,
30426     
30427     
30428     tag: 'div',
30429     cls: '',
30430     bricks: null, //CompositeElement
30431     cols : 0, // array?
30432     // element : null, // wrapped now this.el
30433     _isLayoutInited : null, 
30434     
30435     
30436     getAutoCreate : function(){
30437         
30438         var cfg = {
30439             tag: this.tag,
30440             cls: 'blog-masonary-wrapper ' + this.cls,
30441             cn : {
30442                 cls : 'mas-boxes masonary'
30443             }
30444         };
30445         
30446         return cfg;
30447     },
30448     
30449     getChildContainer: function( )
30450     {
30451         if (this.boxesEl) {
30452             return this.boxesEl;
30453         }
30454         
30455         this.boxesEl = this.el.select('.mas-boxes').first();
30456         
30457         return this.boxesEl;
30458     },
30459     
30460     
30461     initEvents : function()
30462     {
30463         var _this = this;
30464         
30465         if(this.isAutoInitial){
30466             Roo.log('hook children rendered');
30467             this.on('childrenrendered', function() {
30468                 Roo.log('children rendered');
30469                 _this.initial();
30470             } ,this);
30471         }
30472         
30473     },
30474     
30475     initial : function()
30476     {
30477         this.reloadItems();
30478
30479         this.currentSize = this.el.getBox(true);
30480
30481         /// was window resize... - let's see if this works..
30482         Roo.EventManager.onWindowResize(this.resize, this); 
30483
30484         if(!this.isAutoInitial){
30485             this.layout();
30486             return;
30487         }
30488         
30489         this.layout.defer(500,this);
30490     },
30491     
30492     reloadItems: function()
30493     {
30494         this.bricks = this.el.select('.masonry-brick', true);
30495         
30496         this.bricks.each(function(b) {
30497             //Roo.log(b.getSize());
30498             if (!b.attr('originalwidth')) {
30499                 b.attr('originalwidth',  b.getSize().width);
30500             }
30501             
30502         });
30503         
30504         Roo.log(this.bricks.elements.length);
30505     },
30506     
30507     resize : function()
30508     {
30509         Roo.log('resize');
30510         var cs = this.el.getBox(true);
30511         
30512         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30513             Roo.log("no change in with or X");
30514             return;
30515         }
30516         this.currentSize = cs;
30517         this.layout();
30518     },
30519     
30520     layout : function()
30521     {
30522          Roo.log('layout');
30523         this._resetLayout();
30524         //this._manageStamps();
30525       
30526         // don't animate first layout
30527         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30528         this.layoutItems( isInstant );
30529       
30530         // flag for initalized
30531         this._isLayoutInited = true;
30532     },
30533     
30534     layoutItems : function( isInstant )
30535     {
30536         //var items = this._getItemsForLayout( this.items );
30537         // original code supports filtering layout items.. we just ignore it..
30538         
30539         this._layoutItems( this.bricks , isInstant );
30540       
30541         this._postLayout();
30542     },
30543     _layoutItems : function ( items , isInstant)
30544     {
30545        //this.fireEvent( 'layout', this, items );
30546     
30547
30548         if ( !items || !items.elements.length ) {
30549           // no items, emit event with empty array
30550             return;
30551         }
30552
30553         var queue = [];
30554         items.each(function(item) {
30555             Roo.log("layout item");
30556             Roo.log(item);
30557             // get x/y object from method
30558             var position = this._getItemLayoutPosition( item );
30559             // enqueue
30560             position.item = item;
30561             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30562             queue.push( position );
30563         }, this);
30564       
30565         this._processLayoutQueue( queue );
30566     },
30567     /** Sets position of item in DOM
30568     * @param {Element} item
30569     * @param {Number} x - horizontal position
30570     * @param {Number} y - vertical position
30571     * @param {Boolean} isInstant - disables transitions
30572     */
30573     _processLayoutQueue : function( queue )
30574     {
30575         for ( var i=0, len = queue.length; i < len; i++ ) {
30576             var obj = queue[i];
30577             obj.item.position('absolute');
30578             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30579         }
30580     },
30581       
30582     
30583     /**
30584     * Any logic you want to do after each layout,
30585     * i.e. size the container
30586     */
30587     _postLayout : function()
30588     {
30589         this.resizeContainer();
30590     },
30591     
30592     resizeContainer : function()
30593     {
30594         if ( !this.isResizingContainer ) {
30595             return;
30596         }
30597         var size = this._getContainerSize();
30598         if ( size ) {
30599             this.el.setSize(size.width,size.height);
30600             this.boxesEl.setSize(size.width,size.height);
30601         }
30602     },
30603     
30604     
30605     
30606     _resetLayout : function()
30607     {
30608         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30609         this.colWidth = this.el.getWidth();
30610         //this.gutter = this.el.getWidth(); 
30611         
30612         this.measureColumns();
30613
30614         // reset column Y
30615         var i = this.cols;
30616         this.colYs = [];
30617         while (i--) {
30618             this.colYs.push( 0 );
30619         }
30620     
30621         this.maxY = 0;
30622     },
30623
30624     measureColumns : function()
30625     {
30626         this.getContainerWidth();
30627       // if columnWidth is 0, default to outerWidth of first item
30628         if ( !this.columnWidth ) {
30629             var firstItem = this.bricks.first();
30630             Roo.log(firstItem);
30631             this.columnWidth  = this.containerWidth;
30632             if (firstItem && firstItem.attr('originalwidth') ) {
30633                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30634             }
30635             // columnWidth fall back to item of first element
30636             Roo.log("set column width?");
30637                         this.initialColumnWidth = this.columnWidth  ;
30638
30639             // if first elem has no width, default to size of container
30640             
30641         }
30642         
30643         
30644         if (this.initialColumnWidth) {
30645             this.columnWidth = this.initialColumnWidth;
30646         }
30647         
30648         
30649             
30650         // column width is fixed at the top - however if container width get's smaller we should
30651         // reduce it...
30652         
30653         // this bit calcs how man columns..
30654             
30655         var columnWidth = this.columnWidth += this.gutter;
30656       
30657         // calculate columns
30658         var containerWidth = this.containerWidth + this.gutter;
30659         
30660         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30661         // fix rounding errors, typically with gutters
30662         var excess = columnWidth - containerWidth % columnWidth;
30663         
30664         
30665         // if overshoot is less than a pixel, round up, otherwise floor it
30666         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30667         cols = Math[ mathMethod ]( cols );
30668         this.cols = Math.max( cols, 1 );
30669         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30670         
30671          // padding positioning..
30672         var totalColWidth = this.cols * this.columnWidth;
30673         var padavail = this.containerWidth - totalColWidth;
30674         // so for 2 columns - we need 3 'pads'
30675         
30676         var padNeeded = (1+this.cols) * this.padWidth;
30677         
30678         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30679         
30680         this.columnWidth += padExtra
30681         //this.padWidth = Math.floor(padavail /  ( this.cols));
30682         
30683         // adjust colum width so that padding is fixed??
30684         
30685         // we have 3 columns ... total = width * 3
30686         // we have X left over... that should be used by 
30687         
30688         //if (this.expandC) {
30689             
30690         //}
30691         
30692         
30693         
30694     },
30695     
30696     getContainerWidth : function()
30697     {
30698        /* // container is parent if fit width
30699         var container = this.isFitWidth ? this.element.parentNode : this.element;
30700         // check that this.size and size are there
30701         // IE8 triggers resize on body size change, so they might not be
30702         
30703         var size = getSize( container );  //FIXME
30704         this.containerWidth = size && size.innerWidth; //FIXME
30705         */
30706          
30707         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30708         
30709     },
30710     
30711     _getItemLayoutPosition : function( item )  // what is item?
30712     {
30713         // we resize the item to our columnWidth..
30714       
30715         item.setWidth(this.columnWidth);
30716         item.autoBoxAdjust  = false;
30717         
30718         var sz = item.getSize();
30719  
30720         // how many columns does this brick span
30721         var remainder = this.containerWidth % this.columnWidth;
30722         
30723         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30724         // round if off by 1 pixel, otherwise use ceil
30725         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30726         colSpan = Math.min( colSpan, this.cols );
30727         
30728         // normally this should be '1' as we dont' currently allow multi width columns..
30729         
30730         var colGroup = this._getColGroup( colSpan );
30731         // get the minimum Y value from the columns
30732         var minimumY = Math.min.apply( Math, colGroup );
30733         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30734         
30735         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30736          
30737         // position the brick
30738         var position = {
30739             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30740             y: this.currentSize.y + minimumY + this.padHeight
30741         };
30742         
30743         Roo.log(position);
30744         // apply setHeight to necessary columns
30745         var setHeight = minimumY + sz.height + this.padHeight;
30746         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30747         
30748         var setSpan = this.cols + 1 - colGroup.length;
30749         for ( var i = 0; i < setSpan; i++ ) {
30750           this.colYs[ shortColIndex + i ] = setHeight ;
30751         }
30752       
30753         return position;
30754     },
30755     
30756     /**
30757      * @param {Number} colSpan - number of columns the element spans
30758      * @returns {Array} colGroup
30759      */
30760     _getColGroup : function( colSpan )
30761     {
30762         if ( colSpan < 2 ) {
30763           // if brick spans only one column, use all the column Ys
30764           return this.colYs;
30765         }
30766       
30767         var colGroup = [];
30768         // how many different places could this brick fit horizontally
30769         var groupCount = this.cols + 1 - colSpan;
30770         // for each group potential horizontal position
30771         for ( var i = 0; i < groupCount; i++ ) {
30772           // make an array of colY values for that one group
30773           var groupColYs = this.colYs.slice( i, i + colSpan );
30774           // and get the max value of the array
30775           colGroup[i] = Math.max.apply( Math, groupColYs );
30776         }
30777         return colGroup;
30778     },
30779     /*
30780     _manageStamp : function( stamp )
30781     {
30782         var stampSize =  stamp.getSize();
30783         var offset = stamp.getBox();
30784         // get the columns that this stamp affects
30785         var firstX = this.isOriginLeft ? offset.x : offset.right;
30786         var lastX = firstX + stampSize.width;
30787         var firstCol = Math.floor( firstX / this.columnWidth );
30788         firstCol = Math.max( 0, firstCol );
30789         
30790         var lastCol = Math.floor( lastX / this.columnWidth );
30791         // lastCol should not go over if multiple of columnWidth #425
30792         lastCol -= lastX % this.columnWidth ? 0 : 1;
30793         lastCol = Math.min( this.cols - 1, lastCol );
30794         
30795         // set colYs to bottom of the stamp
30796         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30797             stampSize.height;
30798             
30799         for ( var i = firstCol; i <= lastCol; i++ ) {
30800           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30801         }
30802     },
30803     */
30804     
30805     _getContainerSize : function()
30806     {
30807         this.maxY = Math.max.apply( Math, this.colYs );
30808         var size = {
30809             height: this.maxY
30810         };
30811       
30812         if ( this.isFitWidth ) {
30813             size.width = this._getContainerFitWidth();
30814         }
30815       
30816         return size;
30817     },
30818     
30819     _getContainerFitWidth : function()
30820     {
30821         var unusedCols = 0;
30822         // count unused columns
30823         var i = this.cols;
30824         while ( --i ) {
30825           if ( this.colYs[i] !== 0 ) {
30826             break;
30827           }
30828           unusedCols++;
30829         }
30830         // fit container to columns that have been used
30831         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30832     },
30833     
30834     needsResizeLayout : function()
30835     {
30836         var previousWidth = this.containerWidth;
30837         this.getContainerWidth();
30838         return previousWidth !== this.containerWidth;
30839     }
30840  
30841 });
30842
30843  
30844
30845  /*
30846  * - LGPL
30847  *
30848  * element
30849  * 
30850  */
30851
30852 /**
30853  * @class Roo.bootstrap.MasonryBrick
30854  * @extends Roo.bootstrap.Component
30855  * Bootstrap MasonryBrick class
30856  * 
30857  * @constructor
30858  * Create a new MasonryBrick
30859  * @param {Object} config The config object
30860  */
30861
30862 Roo.bootstrap.MasonryBrick = function(config){
30863     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30864     
30865     this.addEvents({
30866         // raw events
30867         /**
30868          * @event click
30869          * When a MasonryBrick is clcik
30870          * @param {Roo.bootstrap.MasonryBrick} this
30871          * @param {Roo.EventObject} e
30872          */
30873         "click" : true
30874     });
30875 };
30876
30877 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30878     
30879     /**
30880      * @cfg {String} title
30881      */   
30882     title : '',
30883     /**
30884      * @cfg {String} html
30885      */   
30886     html : '',
30887     /**
30888      * @cfg {String} bgimage
30889      */   
30890     bgimage : '',
30891     /**
30892      * @cfg {String} videourl
30893      */   
30894     videourl : '',
30895     /**
30896      * @cfg {String} cls
30897      */   
30898     cls : '',
30899     /**
30900      * @cfg {String} href
30901      */   
30902     href : '',
30903     /**
30904      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30905      */   
30906     size : 'xs',
30907     
30908     /**
30909      * @cfg {String} (center|bottom) placetitle
30910      */   
30911     placetitle : '',
30912     
30913     getAutoCreate : function()
30914     {
30915         var cls = 'masonry-brick';
30916         
30917         if(this.href.length){
30918             cls += ' masonry-brick-link';
30919         }
30920         
30921         if(this.bgimage.length){
30922             cls += ' masonry-brick-image';
30923         }
30924         
30925         if(this.size){
30926             cls += ' masonry-' + this.size + '-brick';
30927         }
30928         
30929         if(this.placetitle.length){
30930             
30931             switch (this.placetitle) {
30932                 case 'center' :
30933                     cls += ' masonry-center-title';
30934                     break;
30935                 case 'bottom' :
30936                     cls += ' masonry-bottom-title';
30937                     break;
30938                 default:
30939                     break;
30940             }
30941             
30942         } else {
30943             if(!this.html.length && !this.bgimage.length){
30944                 cls += ' masonry-center-title';
30945             }
30946
30947             if(!this.html.length && this.bgimage.length){
30948                 cls += ' masonry-bottom-title';
30949             }
30950         }
30951         
30952         if(this.cls){
30953             cls += ' ' + this.cls;
30954         }
30955         
30956         var cfg = {
30957             tag: (this.href.length) ? 'a' : 'div',
30958             cls: cls,
30959             cn: [
30960                 {
30961                     tag: 'div',
30962                     cls: 'masonry-brick-paragraph',
30963                     cn: []
30964                 }
30965             ]
30966         };
30967         
30968         if(this.href.length){
30969             cfg.href = this.href;
30970         }
30971         
30972         var cn = cfg.cn[0].cn;
30973         
30974         if(this.title.length){
30975             cn.push({
30976                 tag: 'h4',
30977                 cls: 'masonry-brick-title',
30978                 html: this.title
30979             });
30980         }
30981         
30982         if(this.html.length){
30983             cn.push({
30984                 tag: 'p',
30985                 cls: 'masonry-brick-text',
30986                 html: this.html
30987             });
30988         }  
30989         if (!this.title.length && !this.html.length) {
30990             cfg.cn[0].cls += ' hide';
30991         }
30992         
30993         if(this.bgimage.length){
30994             cfg.cn.push({
30995                 tag: 'img',
30996                 cls: 'masonry-brick-image-view',
30997                 src: this.bgimage
30998             });
30999         }
31000         if(this.videourl.length){
31001             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31002             // youtube support only?
31003             cfg.cn.push({
31004                 tag: 'iframe',
31005                 cls: 'masonry-brick-image-view',
31006                 src: vurl,
31007                 frameborder : 0,
31008                 allowfullscreen : true
31009             });
31010             
31011             
31012         }
31013         return cfg;
31014         
31015     },
31016     
31017     initEvents: function() 
31018     {
31019         switch (this.size) {
31020             case 'xs' :
31021 //                this.intSize = 1;
31022                 this.x = 1;
31023                 this.y = 1;
31024                 break;
31025             case 'sm' :
31026 //                this.intSize = 2;
31027                 this.x = 2;
31028                 this.y = 2;
31029                 break;
31030             case 'md' :
31031             case 'md-left' :
31032             case 'md-right' :
31033 //                this.intSize = 3;
31034                 this.x = 3;
31035                 this.y = 3;
31036                 break;
31037             case 'tall' :
31038 //                this.intSize = 3;
31039                 this.x = 2;
31040                 this.y = 3;
31041                 break;
31042             case 'wide' :
31043 //                this.intSize = 3;
31044                 this.x = 3;
31045                 this.y = 2;
31046                 break;
31047             case 'wide-thin' :
31048 //                this.intSize = 3;
31049                 this.x = 3;
31050                 this.y = 1;
31051                 break;
31052                         
31053             default :
31054                 break;
31055         }
31056         
31057         
31058         
31059         if(Roo.isTouch){
31060             this.el.on('touchstart', this.onTouchStart, this);
31061             this.el.on('touchmove', this.onTouchMove, this);
31062             this.el.on('touchend', this.onTouchEnd, this);
31063             this.el.on('contextmenu', this.onContextMenu, this);
31064         } else {
31065             this.el.on('mouseenter'  ,this.enter, this);
31066             this.el.on('mouseleave', this.leave, this);
31067         }
31068         
31069         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31070             this.parent().bricks.push(this);   
31071         }
31072         
31073     },
31074     
31075     onClick: function(e, el)
31076     {
31077         if(!Roo.isTouch){
31078             return;
31079         }
31080         
31081         var time = this.endTimer - this.startTimer;
31082         
31083         //alert(time);
31084         
31085         if(time < 1000){
31086             return;
31087         }
31088         
31089         e.preventDefault();
31090     },
31091     
31092     enter: function(e, el)
31093     {
31094         e.preventDefault();
31095         
31096         if(this.bgimage.length && this.html.length){
31097             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31098         }
31099     },
31100     
31101     leave: function(e, el)
31102     {
31103         e.preventDefault();
31104         
31105         if(this.bgimage.length && this.html.length){
31106             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31107         }
31108     },
31109     
31110     onTouchStart: function(e, el)
31111     {
31112 //        e.preventDefault();
31113         
31114         this.touchmoved = false;
31115         
31116         if(!this.bgimage.length || !this.html.length){
31117             return;
31118         }
31119         
31120         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31121         
31122         this.timer = new Date().getTime();
31123         
31124     },
31125     
31126     onTouchMove: function(e, el)
31127     {
31128         this.touchmoved = true;
31129     },
31130     
31131     onContextMenu : function(e,el)
31132     {
31133         e.preventDefault();
31134         e.stopPropagation();
31135         return false;
31136     },
31137     
31138     onTouchEnd: function(e, el)
31139     {
31140 //        e.preventDefault();
31141         
31142         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31143         
31144             this.leave(e,el);
31145             
31146             return;
31147         }
31148         
31149         if(!this.bgimage.length || !this.html.length){
31150             
31151             if(this.href.length){
31152                 window.location.href = this.href;
31153             }
31154             
31155             return;
31156         }
31157         
31158         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31159         
31160         window.location.href = this.href;
31161     }
31162     
31163 });
31164
31165  
31166
31167  /*
31168  * - LGPL
31169  *
31170  * element
31171  * 
31172  */
31173
31174 /**
31175  * @class Roo.bootstrap.Brick
31176  * @extends Roo.bootstrap.Component
31177  * Bootstrap Brick class
31178  * 
31179  * @constructor
31180  * Create a new Brick
31181  * @param {Object} config The config object
31182  */
31183
31184 Roo.bootstrap.Brick = function(config){
31185     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31186     
31187     this.addEvents({
31188         // raw events
31189         /**
31190          * @event click
31191          * When a Brick is click
31192          * @param {Roo.bootstrap.Brick} this
31193          * @param {Roo.EventObject} e
31194          */
31195         "click" : true
31196     });
31197 };
31198
31199 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31200     
31201     /**
31202      * @cfg {String} title
31203      */   
31204     title : '',
31205     /**
31206      * @cfg {String} html
31207      */   
31208     html : '',
31209     /**
31210      * @cfg {String} bgimage
31211      */   
31212     bgimage : '',
31213     /**
31214      * @cfg {String} cls
31215      */   
31216     cls : '',
31217     /**
31218      * @cfg {String} href
31219      */   
31220     href : '',
31221     /**
31222      * @cfg {String} video
31223      */   
31224     video : '',
31225     /**
31226      * @cfg {Boolean} square
31227      */   
31228     square : true,
31229     
31230     getAutoCreate : function()
31231     {
31232         var cls = 'roo-brick';
31233         
31234         if(this.href.length){
31235             cls += ' roo-brick-link';
31236         }
31237         
31238         if(this.bgimage.length){
31239             cls += ' roo-brick-image';
31240         }
31241         
31242         if(!this.html.length && !this.bgimage.length){
31243             cls += ' roo-brick-center-title';
31244         }
31245         
31246         if(!this.html.length && this.bgimage.length){
31247             cls += ' roo-brick-bottom-title';
31248         }
31249         
31250         if(this.cls){
31251             cls += ' ' + this.cls;
31252         }
31253         
31254         var cfg = {
31255             tag: (this.href.length) ? 'a' : 'div',
31256             cls: cls,
31257             cn: [
31258                 {
31259                     tag: 'div',
31260                     cls: 'roo-brick-paragraph',
31261                     cn: []
31262                 }
31263             ]
31264         };
31265         
31266         if(this.href.length){
31267             cfg.href = this.href;
31268         }
31269         
31270         var cn = cfg.cn[0].cn;
31271         
31272         if(this.title.length){
31273             cn.push({
31274                 tag: 'h4',
31275                 cls: 'roo-brick-title',
31276                 html: this.title
31277             });
31278         }
31279         
31280         if(this.html.length){
31281             cn.push({
31282                 tag: 'p',
31283                 cls: 'roo-brick-text',
31284                 html: this.html
31285             });
31286         } else {
31287             cn.cls += ' hide';
31288         }
31289         
31290         if(this.bgimage.length){
31291             cfg.cn.push({
31292                 tag: 'img',
31293                 cls: 'roo-brick-image-view',
31294                 src: this.bgimage
31295             });
31296         }
31297         
31298         return cfg;
31299     },
31300     
31301     initEvents: function() 
31302     {
31303         if(this.title.length || this.html.length){
31304             this.el.on('mouseenter'  ,this.enter, this);
31305             this.el.on('mouseleave', this.leave, this);
31306         }
31307         
31308         
31309         Roo.EventManager.onWindowResize(this.resize, this); 
31310         
31311         this.resize();
31312     },
31313     
31314     resize : function()
31315     {
31316         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31317         
31318         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31319 //        paragraph.setHeight(paragraph.getWidth());
31320         
31321         if(this.bgimage.length){
31322             var image = this.el.select('.roo-brick-image-view', true).first();
31323             image.setWidth(paragraph.getWidth());
31324             image.setHeight(paragraph.getWidth());
31325         }
31326         
31327     },
31328     
31329     enter: function(e, el)
31330     {
31331         e.preventDefault();
31332         
31333         if(this.bgimage.length){
31334             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31335             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31336         }
31337     },
31338     
31339     leave: function(e, el)
31340     {
31341         e.preventDefault();
31342         
31343         if(this.bgimage.length){
31344             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31345             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31346         }
31347     }
31348     
31349 });
31350
31351  
31352
31353  /*
31354  * Based on:
31355  * Ext JS Library 1.1.1
31356  * Copyright(c) 2006-2007, Ext JS, LLC.
31357  *
31358  * Originally Released Under LGPL - original licence link has changed is not relivant.
31359  *
31360  * Fork - LGPL
31361  * <script type="text/javascript">
31362  */
31363
31364
31365 /**
31366  * @class Roo.bootstrap.SplitBar
31367  * @extends Roo.util.Observable
31368  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31369  * <br><br>
31370  * Usage:
31371  * <pre><code>
31372 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31373                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31374 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31375 split.minSize = 100;
31376 split.maxSize = 600;
31377 split.animate = true;
31378 split.on('moved', splitterMoved);
31379 </code></pre>
31380  * @constructor
31381  * Create a new SplitBar
31382  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31383  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31384  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31385  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31386                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31387                         position of the SplitBar).
31388  */
31389 Roo.bootstrap.SplitBar = function(cfg){
31390     
31391     /** @private */
31392     
31393     //{
31394     //  dragElement : elm
31395     //  resizingElement: el,
31396         // optional..
31397     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31398     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31399         // existingProxy ???
31400     //}
31401     
31402     this.el = Roo.get(cfg.dragElement, true);
31403     this.el.dom.unselectable = "on";
31404     /** @private */
31405     this.resizingEl = Roo.get(cfg.resizingElement, true);
31406
31407     /**
31408      * @private
31409      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31410      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31411      * @type Number
31412      */
31413     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31414     
31415     /**
31416      * The minimum size of the resizing element. (Defaults to 0)
31417      * @type Number
31418      */
31419     this.minSize = 0;
31420     
31421     /**
31422      * The maximum size of the resizing element. (Defaults to 2000)
31423      * @type Number
31424      */
31425     this.maxSize = 2000;
31426     
31427     /**
31428      * Whether to animate the transition to the new size
31429      * @type Boolean
31430      */
31431     this.animate = false;
31432     
31433     /**
31434      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31435      * @type Boolean
31436      */
31437     this.useShim = false;
31438     
31439     /** @private */
31440     this.shim = null;
31441     
31442     if(!cfg.existingProxy){
31443         /** @private */
31444         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31445     }else{
31446         this.proxy = Roo.get(cfg.existingProxy).dom;
31447     }
31448     /** @private */
31449     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31450     
31451     /** @private */
31452     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31453     
31454     /** @private */
31455     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31456     
31457     /** @private */
31458     this.dragSpecs = {};
31459     
31460     /**
31461      * @private The adapter to use to positon and resize elements
31462      */
31463     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31464     this.adapter.init(this);
31465     
31466     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31467         /** @private */
31468         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31469         this.el.addClass("roo-splitbar-h");
31470     }else{
31471         /** @private */
31472         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31473         this.el.addClass("roo-splitbar-v");
31474     }
31475     
31476     this.addEvents({
31477         /**
31478          * @event resize
31479          * Fires when the splitter is moved (alias for {@link #event-moved})
31480          * @param {Roo.bootstrap.SplitBar} this
31481          * @param {Number} newSize the new width or height
31482          */
31483         "resize" : true,
31484         /**
31485          * @event moved
31486          * Fires when the splitter is moved
31487          * @param {Roo.bootstrap.SplitBar} this
31488          * @param {Number} newSize the new width or height
31489          */
31490         "moved" : true,
31491         /**
31492          * @event beforeresize
31493          * Fires before the splitter is dragged
31494          * @param {Roo.bootstrap.SplitBar} this
31495          */
31496         "beforeresize" : true,
31497
31498         "beforeapply" : true
31499     });
31500
31501     Roo.util.Observable.call(this);
31502 };
31503
31504 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31505     onStartProxyDrag : function(x, y){
31506         this.fireEvent("beforeresize", this);
31507         if(!this.overlay){
31508             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31509             o.unselectable();
31510             o.enableDisplayMode("block");
31511             // all splitbars share the same overlay
31512             Roo.bootstrap.SplitBar.prototype.overlay = o;
31513         }
31514         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31515         this.overlay.show();
31516         Roo.get(this.proxy).setDisplayed("block");
31517         var size = this.adapter.getElementSize(this);
31518         this.activeMinSize = this.getMinimumSize();;
31519         this.activeMaxSize = this.getMaximumSize();;
31520         var c1 = size - this.activeMinSize;
31521         var c2 = Math.max(this.activeMaxSize - size, 0);
31522         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31523             this.dd.resetConstraints();
31524             this.dd.setXConstraint(
31525                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31526                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31527             );
31528             this.dd.setYConstraint(0, 0);
31529         }else{
31530             this.dd.resetConstraints();
31531             this.dd.setXConstraint(0, 0);
31532             this.dd.setYConstraint(
31533                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31534                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31535             );
31536          }
31537         this.dragSpecs.startSize = size;
31538         this.dragSpecs.startPoint = [x, y];
31539         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31540     },
31541     
31542     /** 
31543      * @private Called after the drag operation by the DDProxy
31544      */
31545     onEndProxyDrag : function(e){
31546         Roo.get(this.proxy).setDisplayed(false);
31547         var endPoint = Roo.lib.Event.getXY(e);
31548         if(this.overlay){
31549             this.overlay.hide();
31550         }
31551         var newSize;
31552         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31553             newSize = this.dragSpecs.startSize + 
31554                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31555                     endPoint[0] - this.dragSpecs.startPoint[0] :
31556                     this.dragSpecs.startPoint[0] - endPoint[0]
31557                 );
31558         }else{
31559             newSize = this.dragSpecs.startSize + 
31560                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31561                     endPoint[1] - this.dragSpecs.startPoint[1] :
31562                     this.dragSpecs.startPoint[1] - endPoint[1]
31563                 );
31564         }
31565         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31566         if(newSize != this.dragSpecs.startSize){
31567             if(this.fireEvent('beforeapply', this, newSize) !== false){
31568                 this.adapter.setElementSize(this, newSize);
31569                 this.fireEvent("moved", this, newSize);
31570                 this.fireEvent("resize", this, newSize);
31571             }
31572         }
31573     },
31574     
31575     /**
31576      * Get the adapter this SplitBar uses
31577      * @return The adapter object
31578      */
31579     getAdapter : function(){
31580         return this.adapter;
31581     },
31582     
31583     /**
31584      * Set the adapter this SplitBar uses
31585      * @param {Object} adapter A SplitBar adapter object
31586      */
31587     setAdapter : function(adapter){
31588         this.adapter = adapter;
31589         this.adapter.init(this);
31590     },
31591     
31592     /**
31593      * Gets the minimum size for the resizing element
31594      * @return {Number} The minimum size
31595      */
31596     getMinimumSize : function(){
31597         return this.minSize;
31598     },
31599     
31600     /**
31601      * Sets the minimum size for the resizing element
31602      * @param {Number} minSize The minimum size
31603      */
31604     setMinimumSize : function(minSize){
31605         this.minSize = minSize;
31606     },
31607     
31608     /**
31609      * Gets the maximum size for the resizing element
31610      * @return {Number} The maximum size
31611      */
31612     getMaximumSize : function(){
31613         return this.maxSize;
31614     },
31615     
31616     /**
31617      * Sets the maximum size for the resizing element
31618      * @param {Number} maxSize The maximum size
31619      */
31620     setMaximumSize : function(maxSize){
31621         this.maxSize = maxSize;
31622     },
31623     
31624     /**
31625      * Sets the initialize size for the resizing element
31626      * @param {Number} size The initial size
31627      */
31628     setCurrentSize : function(size){
31629         var oldAnimate = this.animate;
31630         this.animate = false;
31631         this.adapter.setElementSize(this, size);
31632         this.animate = oldAnimate;
31633     },
31634     
31635     /**
31636      * Destroy this splitbar. 
31637      * @param {Boolean} removeEl True to remove the element
31638      */
31639     destroy : function(removeEl){
31640         if(this.shim){
31641             this.shim.remove();
31642         }
31643         this.dd.unreg();
31644         this.proxy.parentNode.removeChild(this.proxy);
31645         if(removeEl){
31646             this.el.remove();
31647         }
31648     }
31649 });
31650
31651 /**
31652  * @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.
31653  */
31654 Roo.bootstrap.SplitBar.createProxy = function(dir){
31655     var proxy = new Roo.Element(document.createElement("div"));
31656     proxy.unselectable();
31657     var cls = 'roo-splitbar-proxy';
31658     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31659     document.body.appendChild(proxy.dom);
31660     return proxy.dom;
31661 };
31662
31663 /** 
31664  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31665  * Default Adapter. It assumes the splitter and resizing element are not positioned
31666  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31667  */
31668 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31669 };
31670
31671 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31672     // do nothing for now
31673     init : function(s){
31674     
31675     },
31676     /**
31677      * Called before drag operations to get the current size of the resizing element. 
31678      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31679      */
31680      getElementSize : function(s){
31681         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31682             return s.resizingEl.getWidth();
31683         }else{
31684             return s.resizingEl.getHeight();
31685         }
31686     },
31687     
31688     /**
31689      * Called after drag operations to set the size of the resizing element.
31690      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31691      * @param {Number} newSize The new size to set
31692      * @param {Function} onComplete A function to be invoked when resizing is complete
31693      */
31694     setElementSize : function(s, newSize, onComplete){
31695         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31696             if(!s.animate){
31697                 s.resizingEl.setWidth(newSize);
31698                 if(onComplete){
31699                     onComplete(s, newSize);
31700                 }
31701             }else{
31702                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31703             }
31704         }else{
31705             
31706             if(!s.animate){
31707                 s.resizingEl.setHeight(newSize);
31708                 if(onComplete){
31709                     onComplete(s, newSize);
31710                 }
31711             }else{
31712                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31713             }
31714         }
31715     }
31716 };
31717
31718 /** 
31719  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31720  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31721  * Adapter that  moves the splitter element to align with the resized sizing element. 
31722  * Used with an absolute positioned SplitBar.
31723  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31724  * document.body, make sure you assign an id to the body element.
31725  */
31726 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31727     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31728     this.container = Roo.get(container);
31729 };
31730
31731 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31732     init : function(s){
31733         this.basic.init(s);
31734     },
31735     
31736     getElementSize : function(s){
31737         return this.basic.getElementSize(s);
31738     },
31739     
31740     setElementSize : function(s, newSize, onComplete){
31741         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31742     },
31743     
31744     moveSplitter : function(s){
31745         var yes = Roo.bootstrap.SplitBar;
31746         switch(s.placement){
31747             case yes.LEFT:
31748                 s.el.setX(s.resizingEl.getRight());
31749                 break;
31750             case yes.RIGHT:
31751                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31752                 break;
31753             case yes.TOP:
31754                 s.el.setY(s.resizingEl.getBottom());
31755                 break;
31756             case yes.BOTTOM:
31757                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31758                 break;
31759         }
31760     }
31761 };
31762
31763 /**
31764  * Orientation constant - Create a vertical SplitBar
31765  * @static
31766  * @type Number
31767  */
31768 Roo.bootstrap.SplitBar.VERTICAL = 1;
31769
31770 /**
31771  * Orientation constant - Create a horizontal SplitBar
31772  * @static
31773  * @type Number
31774  */
31775 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31776
31777 /**
31778  * Placement constant - The resizing element is to the left of the splitter element
31779  * @static
31780  * @type Number
31781  */
31782 Roo.bootstrap.SplitBar.LEFT = 1;
31783
31784 /**
31785  * Placement constant - The resizing element is to the right of the splitter element
31786  * @static
31787  * @type Number
31788  */
31789 Roo.bootstrap.SplitBar.RIGHT = 2;
31790
31791 /**
31792  * Placement constant - The resizing element is positioned above the splitter element
31793  * @static
31794  * @type Number
31795  */
31796 Roo.bootstrap.SplitBar.TOP = 3;
31797
31798 /**
31799  * Placement constant - The resizing element is positioned under splitter element
31800  * @static
31801  * @type Number
31802  */
31803 Roo.bootstrap.SplitBar.BOTTOM = 4;
31804 Roo.namespace("Roo.bootstrap.layout");/*
31805  * Based on:
31806  * Ext JS Library 1.1.1
31807  * Copyright(c) 2006-2007, Ext JS, LLC.
31808  *
31809  * Originally Released Under LGPL - original licence link has changed is not relivant.
31810  *
31811  * Fork - LGPL
31812  * <script type="text/javascript">
31813  */
31814  
31815 /**
31816  * @class Roo.bootstrap.layout.Manager
31817  * @extends Roo.bootstrap.Component
31818  * Base class for layout managers.
31819  */
31820 Roo.bootstrap.layout.Manager = function(config)
31821 {
31822     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31823     
31824     
31825      
31826     
31827     
31828     /** false to disable window resize monitoring @type Boolean */
31829     this.monitorWindowResize = true;
31830     this.regions = {};
31831     this.addEvents({
31832         /**
31833          * @event layout
31834          * Fires when a layout is performed. 
31835          * @param {Roo.LayoutManager} this
31836          */
31837         "layout" : true,
31838         /**
31839          * @event regionresized
31840          * Fires when the user resizes a region. 
31841          * @param {Roo.LayoutRegion} region The resized region
31842          * @param {Number} newSize The new size (width for east/west, height for north/south)
31843          */
31844         "regionresized" : true,
31845         /**
31846          * @event regioncollapsed
31847          * Fires when a region is collapsed. 
31848          * @param {Roo.LayoutRegion} region The collapsed region
31849          */
31850         "regioncollapsed" : true,
31851         /**
31852          * @event regionexpanded
31853          * Fires when a region is expanded.  
31854          * @param {Roo.LayoutRegion} region The expanded region
31855          */
31856         "regionexpanded" : true
31857     });
31858     this.updating = false;
31859     
31860     if (config.el) {
31861         this.el = Roo.get(config.el);
31862         this.initEvents();
31863     }
31864     
31865 };
31866
31867 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31868     
31869     
31870     regions : null,
31871     
31872     monitorWindowResize : true,
31873     
31874     
31875     updating : false,
31876     
31877     
31878     onRender : function(ct, position)
31879     {
31880         if(!this.el){
31881             this.el = Roo.get(ct);
31882             this.initEvents();
31883         }
31884     },
31885     
31886     
31887     initEvents: function()
31888     {
31889         
31890         
31891         // ie scrollbar fix
31892         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31893             document.body.scroll = "no";
31894         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31895             this.el.position('relative');
31896         }
31897         this.id = this.el.id;
31898         this.el.addClass("roo-layout-container");
31899         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31900         if(this.el.dom != document.body ) {
31901             this.el.on('resize', this.layout,this);
31902             this.el.on('show', this.layout,this);
31903         }
31904
31905     },
31906     
31907     /**
31908      * Returns true if this layout is currently being updated
31909      * @return {Boolean}
31910      */
31911     isUpdating : function(){
31912         return this.updating; 
31913     },
31914     
31915     /**
31916      * Suspend the LayoutManager from doing auto-layouts while
31917      * making multiple add or remove calls
31918      */
31919     beginUpdate : function(){
31920         this.updating = true;    
31921     },
31922     
31923     /**
31924      * Restore auto-layouts and optionally disable the manager from performing a layout
31925      * @param {Boolean} noLayout true to disable a layout update 
31926      */
31927     endUpdate : function(noLayout){
31928         this.updating = false;
31929         if(!noLayout){
31930             this.layout();
31931         }    
31932     },
31933     
31934     layout: function(){
31935         // abstract...
31936     },
31937     
31938     onRegionResized : function(region, newSize){
31939         this.fireEvent("regionresized", region, newSize);
31940         this.layout();
31941     },
31942     
31943     onRegionCollapsed : function(region){
31944         this.fireEvent("regioncollapsed", region);
31945     },
31946     
31947     onRegionExpanded : function(region){
31948         this.fireEvent("regionexpanded", region);
31949     },
31950         
31951     /**
31952      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31953      * performs box-model adjustments.
31954      * @return {Object} The size as an object {width: (the width), height: (the height)}
31955      */
31956     getViewSize : function()
31957     {
31958         var size;
31959         if(this.el.dom != document.body){
31960             size = this.el.getSize();
31961         }else{
31962             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31963         }
31964         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31965         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31966         return size;
31967     },
31968     
31969     /**
31970      * Returns the Element this layout is bound to.
31971      * @return {Roo.Element}
31972      */
31973     getEl : function(){
31974         return this.el;
31975     },
31976     
31977     /**
31978      * Returns the specified region.
31979      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31980      * @return {Roo.LayoutRegion}
31981      */
31982     getRegion : function(target){
31983         return this.regions[target.toLowerCase()];
31984     },
31985     
31986     onWindowResize : function(){
31987         if(this.monitorWindowResize){
31988             this.layout();
31989         }
31990     }
31991 });/*
31992  * Based on:
31993  * Ext JS Library 1.1.1
31994  * Copyright(c) 2006-2007, Ext JS, LLC.
31995  *
31996  * Originally Released Under LGPL - original licence link has changed is not relivant.
31997  *
31998  * Fork - LGPL
31999  * <script type="text/javascript">
32000  */
32001 /**
32002  * @class Roo.bootstrap.layout.Border
32003  * @extends Roo.bootstrap.layout.Manager
32004  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32005  * please see: examples/bootstrap/nested.html<br><br>
32006  
32007 <b>The container the layout is rendered into can be either the body element or any other element.
32008 If it is not the body element, the container needs to either be an absolute positioned element,
32009 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32010 the container size if it is not the body element.</b>
32011
32012 * @constructor
32013 * Create a new Border
32014 * @param {Object} config Configuration options
32015  */
32016 Roo.bootstrap.layout.Border = function(config){
32017     config = config || {};
32018     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32019     
32020     
32021     
32022     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32023         if(config[region]){
32024             config[region].region = region;
32025             this.addRegion(config[region]);
32026         }
32027     },this);
32028     
32029 };
32030
32031 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32032
32033 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32034     /**
32035      * Creates and adds a new region if it doesn't already exist.
32036      * @param {String} target The target region key (north, south, east, west or center).
32037      * @param {Object} config The regions config object
32038      * @return {BorderLayoutRegion} The new region
32039      */
32040     addRegion : function(config)
32041     {
32042         if(!this.regions[config.region]){
32043             var r = this.factory(config);
32044             this.bindRegion(r);
32045         }
32046         return this.regions[config.region];
32047     },
32048
32049     // private (kinda)
32050     bindRegion : function(r){
32051         this.regions[r.config.region] = r;
32052         
32053         r.on("visibilitychange",    this.layout, this);
32054         r.on("paneladded",          this.layout, this);
32055         r.on("panelremoved",        this.layout, this);
32056         r.on("invalidated",         this.layout, this);
32057         r.on("resized",             this.onRegionResized, this);
32058         r.on("collapsed",           this.onRegionCollapsed, this);
32059         r.on("expanded",            this.onRegionExpanded, this);
32060     },
32061
32062     /**
32063      * Performs a layout update.
32064      */
32065     layout : function()
32066     {
32067         if(this.updating) {
32068             return;
32069         }
32070         
32071         // render all the rebions if they have not been done alreayd?
32072         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32073             if(this.regions[region] && !this.regions[region].bodyEl){
32074                 this.regions[region].onRender(this.el)
32075             }
32076         },this);
32077         
32078         var size = this.getViewSize();
32079         var w = size.width;
32080         var h = size.height;
32081         var centerW = w;
32082         var centerH = h;
32083         var centerY = 0;
32084         var centerX = 0;
32085         //var x = 0, y = 0;
32086
32087         var rs = this.regions;
32088         var north = rs["north"];
32089         var south = rs["south"]; 
32090         var west = rs["west"];
32091         var east = rs["east"];
32092         var center = rs["center"];
32093         //if(this.hideOnLayout){ // not supported anymore
32094             //c.el.setStyle("display", "none");
32095         //}
32096         if(north && north.isVisible()){
32097             var b = north.getBox();
32098             var m = north.getMargins();
32099             b.width = w - (m.left+m.right);
32100             b.x = m.left;
32101             b.y = m.top;
32102             centerY = b.height + b.y + m.bottom;
32103             centerH -= centerY;
32104             north.updateBox(this.safeBox(b));
32105         }
32106         if(south && south.isVisible()){
32107             var b = south.getBox();
32108             var m = south.getMargins();
32109             b.width = w - (m.left+m.right);
32110             b.x = m.left;
32111             var totalHeight = (b.height + m.top + m.bottom);
32112             b.y = h - totalHeight + m.top;
32113             centerH -= totalHeight;
32114             south.updateBox(this.safeBox(b));
32115         }
32116         if(west && west.isVisible()){
32117             var b = west.getBox();
32118             var m = west.getMargins();
32119             b.height = centerH - (m.top+m.bottom);
32120             b.x = m.left;
32121             b.y = centerY + m.top;
32122             var totalWidth = (b.width + m.left + m.right);
32123             centerX += totalWidth;
32124             centerW -= totalWidth;
32125             west.updateBox(this.safeBox(b));
32126         }
32127         if(east && east.isVisible()){
32128             var b = east.getBox();
32129             var m = east.getMargins();
32130             b.height = centerH - (m.top+m.bottom);
32131             var totalWidth = (b.width + m.left + m.right);
32132             b.x = w - totalWidth + m.left;
32133             b.y = centerY + m.top;
32134             centerW -= totalWidth;
32135             east.updateBox(this.safeBox(b));
32136         }
32137         if(center){
32138             var m = center.getMargins();
32139             var centerBox = {
32140                 x: centerX + m.left,
32141                 y: centerY + m.top,
32142                 width: centerW - (m.left+m.right),
32143                 height: centerH - (m.top+m.bottom)
32144             };
32145             //if(this.hideOnLayout){
32146                 //center.el.setStyle("display", "block");
32147             //}
32148             center.updateBox(this.safeBox(centerBox));
32149         }
32150         this.el.repaint();
32151         this.fireEvent("layout", this);
32152     },
32153
32154     // private
32155     safeBox : function(box){
32156         box.width = Math.max(0, box.width);
32157         box.height = Math.max(0, box.height);
32158         return box;
32159     },
32160
32161     /**
32162      * Adds a ContentPanel (or subclass) to this layout.
32163      * @param {String} target The target region key (north, south, east, west or center).
32164      * @param {Roo.ContentPanel} panel The panel to add
32165      * @return {Roo.ContentPanel} The added panel
32166      */
32167     add : function(target, panel){
32168          
32169         target = target.toLowerCase();
32170         return this.regions[target].add(panel);
32171     },
32172
32173     /**
32174      * Remove a ContentPanel (or subclass) to this layout.
32175      * @param {String} target The target region key (north, south, east, west or center).
32176      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32177      * @return {Roo.ContentPanel} The removed panel
32178      */
32179     remove : function(target, panel){
32180         target = target.toLowerCase();
32181         return this.regions[target].remove(panel);
32182     },
32183
32184     /**
32185      * Searches all regions for a panel with the specified id
32186      * @param {String} panelId
32187      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32188      */
32189     findPanel : function(panelId){
32190         var rs = this.regions;
32191         for(var target in rs){
32192             if(typeof rs[target] != "function"){
32193                 var p = rs[target].getPanel(panelId);
32194                 if(p){
32195                     return p;
32196                 }
32197             }
32198         }
32199         return null;
32200     },
32201
32202     /**
32203      * Searches all regions for a panel with the specified id and activates (shows) it.
32204      * @param {String/ContentPanel} panelId The panels id or the panel itself
32205      * @return {Roo.ContentPanel} The shown panel or null
32206      */
32207     showPanel : function(panelId) {
32208       var rs = this.regions;
32209       for(var target in rs){
32210          var r = rs[target];
32211          if(typeof r != "function"){
32212             if(r.hasPanel(panelId)){
32213                return r.showPanel(panelId);
32214             }
32215          }
32216       }
32217       return null;
32218    },
32219
32220    /**
32221      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32222      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32223      */
32224    /*
32225     restoreState : function(provider){
32226         if(!provider){
32227             provider = Roo.state.Manager;
32228         }
32229         var sm = new Roo.LayoutStateManager();
32230         sm.init(this, provider);
32231     },
32232 */
32233  
32234  
32235     /**
32236      * Adds a xtype elements to the layout.
32237      * <pre><code>
32238
32239 layout.addxtype({
32240        xtype : 'ContentPanel',
32241        region: 'west',
32242        items: [ .... ]
32243    }
32244 );
32245
32246 layout.addxtype({
32247         xtype : 'NestedLayoutPanel',
32248         region: 'west',
32249         layout: {
32250            center: { },
32251            west: { }   
32252         },
32253         items : [ ... list of content panels or nested layout panels.. ]
32254    }
32255 );
32256 </code></pre>
32257      * @param {Object} cfg Xtype definition of item to add.
32258      */
32259     addxtype : function(cfg)
32260     {
32261         // basically accepts a pannel...
32262         // can accept a layout region..!?!?
32263         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32264         
32265         
32266         // theory?  children can only be panels??
32267         
32268         //if (!cfg.xtype.match(/Panel$/)) {
32269         //    return false;
32270         //}
32271         var ret = false;
32272         
32273         if (typeof(cfg.region) == 'undefined') {
32274             Roo.log("Failed to add Panel, region was not set");
32275             Roo.log(cfg);
32276             return false;
32277         }
32278         var region = cfg.region;
32279         delete cfg.region;
32280         
32281           
32282         var xitems = [];
32283         if (cfg.items) {
32284             xitems = cfg.items;
32285             delete cfg.items;
32286         }
32287         var nb = false;
32288         
32289         switch(cfg.xtype) 
32290         {
32291             case 'Content':  // ContentPanel (el, cfg)
32292             case 'Scroll':  // ContentPanel (el, cfg)
32293             case 'View': 
32294                 cfg.autoCreate = true;
32295                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32296                 //} else {
32297                 //    var el = this.el.createChild();
32298                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32299                 //}
32300                 
32301                 this.add(region, ret);
32302                 break;
32303             
32304             /*
32305             case 'TreePanel': // our new panel!
32306                 cfg.el = this.el.createChild();
32307                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32308                 this.add(region, ret);
32309                 break;
32310             */
32311             
32312             case 'Nest': 
32313                 // create a new Layout (which is  a Border Layout...
32314                 
32315                 var clayout = cfg.layout;
32316                 clayout.el  = this.el.createChild();
32317                 clayout.items   = clayout.items  || [];
32318                 
32319                 delete cfg.layout;
32320                 
32321                 // replace this exitems with the clayout ones..
32322                 xitems = clayout.items;
32323                  
32324                 // force background off if it's in center...
32325                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32326                     cfg.background = false;
32327                 }
32328                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32329                 
32330                 
32331                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32332                 //console.log('adding nested layout panel '  + cfg.toSource());
32333                 this.add(region, ret);
32334                 nb = {}; /// find first...
32335                 break;
32336             
32337             case 'Grid':
32338                 
32339                 // needs grid and region
32340                 
32341                 //var el = this.getRegion(region).el.createChild();
32342                 /*
32343                  *var el = this.el.createChild();
32344                 // create the grid first...
32345                 cfg.grid.container = el;
32346                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32347                 */
32348                 
32349                 if (region == 'center' && this.active ) {
32350                     cfg.background = false;
32351                 }
32352                 
32353                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32354                 
32355                 this.add(region, ret);
32356                 /*
32357                 if (cfg.background) {
32358                     // render grid on panel activation (if panel background)
32359                     ret.on('activate', function(gp) {
32360                         if (!gp.grid.rendered) {
32361                     //        gp.grid.render(el);
32362                         }
32363                     });
32364                 } else {
32365                   //  cfg.grid.render(el);
32366                 }
32367                 */
32368                 break;
32369            
32370            
32371             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32372                 // it was the old xcomponent building that caused this before.
32373                 // espeically if border is the top element in the tree.
32374                 ret = this;
32375                 break; 
32376                 
32377                     
32378                 
32379                 
32380                 
32381             default:
32382                 /*
32383                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32384                     
32385                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32386                     this.add(region, ret);
32387                 } else {
32388                 */
32389                     Roo.log(cfg);
32390                     throw "Can not add '" + cfg.xtype + "' to Border";
32391                     return null;
32392              
32393                                 
32394              
32395         }
32396         this.beginUpdate();
32397         // add children..
32398         var region = '';
32399         var abn = {};
32400         Roo.each(xitems, function(i)  {
32401             region = nb && i.region ? i.region : false;
32402             
32403             var add = ret.addxtype(i);
32404            
32405             if (region) {
32406                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32407                 if (!i.background) {
32408                     abn[region] = nb[region] ;
32409                 }
32410             }
32411             
32412         });
32413         this.endUpdate();
32414
32415         // make the last non-background panel active..
32416         //if (nb) { Roo.log(abn); }
32417         if (nb) {
32418             
32419             for(var r in abn) {
32420                 region = this.getRegion(r);
32421                 if (region) {
32422                     // tried using nb[r], but it does not work..
32423                      
32424                     region.showPanel(abn[r]);
32425                    
32426                 }
32427             }
32428         }
32429         return ret;
32430         
32431     },
32432     
32433     
32434 // private
32435     factory : function(cfg)
32436     {
32437         
32438         var validRegions = Roo.bootstrap.layout.Border.regions;
32439
32440         var target = cfg.region;
32441         cfg.mgr = this;
32442         
32443         var r = Roo.bootstrap.layout;
32444         Roo.log(target);
32445         switch(target){
32446             case "north":
32447                 return new r.North(cfg);
32448             case "south":
32449                 return new r.South(cfg);
32450             case "east":
32451                 return new r.East(cfg);
32452             case "west":
32453                 return new r.West(cfg);
32454             case "center":
32455                 return new r.Center(cfg);
32456         }
32457         throw 'Layout region "'+target+'" not supported.';
32458     }
32459     
32460     
32461 });
32462  /*
32463  * Based on:
32464  * Ext JS Library 1.1.1
32465  * Copyright(c) 2006-2007, Ext JS, LLC.
32466  *
32467  * Originally Released Under LGPL - original licence link has changed is not relivant.
32468  *
32469  * Fork - LGPL
32470  * <script type="text/javascript">
32471  */
32472  
32473 /**
32474  * @class Roo.bootstrap.layout.Basic
32475  * @extends Roo.util.Observable
32476  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32477  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32478  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32479  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32480  * @cfg {string}   region  the region that it inhabits..
32481  * @cfg {bool}   skipConfig skip config?
32482  * 
32483
32484  */
32485 Roo.bootstrap.layout.Basic = function(config){
32486     
32487     this.mgr = config.mgr;
32488     
32489     this.position = config.region;
32490     
32491     var skipConfig = config.skipConfig;
32492     
32493     this.events = {
32494         /**
32495          * @scope Roo.BasicLayoutRegion
32496          */
32497         
32498         /**
32499          * @event beforeremove
32500          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32501          * @param {Roo.LayoutRegion} this
32502          * @param {Roo.ContentPanel} panel The panel
32503          * @param {Object} e The cancel event object
32504          */
32505         "beforeremove" : true,
32506         /**
32507          * @event invalidated
32508          * Fires when the layout for this region is changed.
32509          * @param {Roo.LayoutRegion} this
32510          */
32511         "invalidated" : true,
32512         /**
32513          * @event visibilitychange
32514          * Fires when this region is shown or hidden 
32515          * @param {Roo.LayoutRegion} this
32516          * @param {Boolean} visibility true or false
32517          */
32518         "visibilitychange" : true,
32519         /**
32520          * @event paneladded
32521          * Fires when a panel is added. 
32522          * @param {Roo.LayoutRegion} this
32523          * @param {Roo.ContentPanel} panel The panel
32524          */
32525         "paneladded" : true,
32526         /**
32527          * @event panelremoved
32528          * Fires when a panel is removed. 
32529          * @param {Roo.LayoutRegion} this
32530          * @param {Roo.ContentPanel} panel The panel
32531          */
32532         "panelremoved" : true,
32533         /**
32534          * @event beforecollapse
32535          * Fires when this region before collapse.
32536          * @param {Roo.LayoutRegion} this
32537          */
32538         "beforecollapse" : true,
32539         /**
32540          * @event collapsed
32541          * Fires when this region is collapsed.
32542          * @param {Roo.LayoutRegion} this
32543          */
32544         "collapsed" : true,
32545         /**
32546          * @event expanded
32547          * Fires when this region is expanded.
32548          * @param {Roo.LayoutRegion} this
32549          */
32550         "expanded" : true,
32551         /**
32552          * @event slideshow
32553          * Fires when this region is slid into view.
32554          * @param {Roo.LayoutRegion} this
32555          */
32556         "slideshow" : true,
32557         /**
32558          * @event slidehide
32559          * Fires when this region slides out of view. 
32560          * @param {Roo.LayoutRegion} this
32561          */
32562         "slidehide" : true,
32563         /**
32564          * @event panelactivated
32565          * Fires when a panel is activated. 
32566          * @param {Roo.LayoutRegion} this
32567          * @param {Roo.ContentPanel} panel The activated panel
32568          */
32569         "panelactivated" : true,
32570         /**
32571          * @event resized
32572          * Fires when the user resizes this region. 
32573          * @param {Roo.LayoutRegion} this
32574          * @param {Number} newSize The new size (width for east/west, height for north/south)
32575          */
32576         "resized" : true
32577     };
32578     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32579     this.panels = new Roo.util.MixedCollection();
32580     this.panels.getKey = this.getPanelId.createDelegate(this);
32581     this.box = null;
32582     this.activePanel = null;
32583     // ensure listeners are added...
32584     
32585     if (config.listeners || config.events) {
32586         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32587             listeners : config.listeners || {},
32588             events : config.events || {}
32589         });
32590     }
32591     
32592     if(skipConfig !== true){
32593         this.applyConfig(config);
32594     }
32595 };
32596
32597 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32598 {
32599     getPanelId : function(p){
32600         return p.getId();
32601     },
32602     
32603     applyConfig : function(config){
32604         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32605         this.config = config;
32606         
32607     },
32608     
32609     /**
32610      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32611      * the width, for horizontal (north, south) the height.
32612      * @param {Number} newSize The new width or height
32613      */
32614     resizeTo : function(newSize){
32615         var el = this.el ? this.el :
32616                  (this.activePanel ? this.activePanel.getEl() : null);
32617         if(el){
32618             switch(this.position){
32619                 case "east":
32620                 case "west":
32621                     el.setWidth(newSize);
32622                     this.fireEvent("resized", this, newSize);
32623                 break;
32624                 case "north":
32625                 case "south":
32626                     el.setHeight(newSize);
32627                     this.fireEvent("resized", this, newSize);
32628                 break;                
32629             }
32630         }
32631     },
32632     
32633     getBox : function(){
32634         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32635     },
32636     
32637     getMargins : function(){
32638         return this.margins;
32639     },
32640     
32641     updateBox : function(box){
32642         this.box = box;
32643         var el = this.activePanel.getEl();
32644         el.dom.style.left = box.x + "px";
32645         el.dom.style.top = box.y + "px";
32646         this.activePanel.setSize(box.width, box.height);
32647     },
32648     
32649     /**
32650      * Returns the container element for this region.
32651      * @return {Roo.Element}
32652      */
32653     getEl : function(){
32654         return this.activePanel;
32655     },
32656     
32657     /**
32658      * Returns true if this region is currently visible.
32659      * @return {Boolean}
32660      */
32661     isVisible : function(){
32662         return this.activePanel ? true : false;
32663     },
32664     
32665     setActivePanel : function(panel){
32666         panel = this.getPanel(panel);
32667         if(this.activePanel && this.activePanel != panel){
32668             this.activePanel.setActiveState(false);
32669             this.activePanel.getEl().setLeftTop(-10000,-10000);
32670         }
32671         this.activePanel = panel;
32672         panel.setActiveState(true);
32673         if(this.box){
32674             panel.setSize(this.box.width, this.box.height);
32675         }
32676         this.fireEvent("panelactivated", this, panel);
32677         this.fireEvent("invalidated");
32678     },
32679     
32680     /**
32681      * Show the specified panel.
32682      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32683      * @return {Roo.ContentPanel} The shown panel or null
32684      */
32685     showPanel : function(panel){
32686         panel = this.getPanel(panel);
32687         if(panel){
32688             this.setActivePanel(panel);
32689         }
32690         return panel;
32691     },
32692     
32693     /**
32694      * Get the active panel for this region.
32695      * @return {Roo.ContentPanel} The active panel or null
32696      */
32697     getActivePanel : function(){
32698         return this.activePanel;
32699     },
32700     
32701     /**
32702      * Add the passed ContentPanel(s)
32703      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32704      * @return {Roo.ContentPanel} The panel added (if only one was added)
32705      */
32706     add : function(panel){
32707         if(arguments.length > 1){
32708             for(var i = 0, len = arguments.length; i < len; i++) {
32709                 this.add(arguments[i]);
32710             }
32711             return null;
32712         }
32713         if(this.hasPanel(panel)){
32714             this.showPanel(panel);
32715             return panel;
32716         }
32717         var el = panel.getEl();
32718         if(el.dom.parentNode != this.mgr.el.dom){
32719             this.mgr.el.dom.appendChild(el.dom);
32720         }
32721         if(panel.setRegion){
32722             panel.setRegion(this);
32723         }
32724         this.panels.add(panel);
32725         el.setStyle("position", "absolute");
32726         if(!panel.background){
32727             this.setActivePanel(panel);
32728             if(this.config.initialSize && this.panels.getCount()==1){
32729                 this.resizeTo(this.config.initialSize);
32730             }
32731         }
32732         this.fireEvent("paneladded", this, panel);
32733         return panel;
32734     },
32735     
32736     /**
32737      * Returns true if the panel is in this region.
32738      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32739      * @return {Boolean}
32740      */
32741     hasPanel : function(panel){
32742         if(typeof panel == "object"){ // must be panel obj
32743             panel = panel.getId();
32744         }
32745         return this.getPanel(panel) ? true : false;
32746     },
32747     
32748     /**
32749      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32750      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32751      * @param {Boolean} preservePanel Overrides the config preservePanel option
32752      * @return {Roo.ContentPanel} The panel that was removed
32753      */
32754     remove : function(panel, preservePanel){
32755         panel = this.getPanel(panel);
32756         if(!panel){
32757             return null;
32758         }
32759         var e = {};
32760         this.fireEvent("beforeremove", this, panel, e);
32761         if(e.cancel === true){
32762             return null;
32763         }
32764         var panelId = panel.getId();
32765         this.panels.removeKey(panelId);
32766         return panel;
32767     },
32768     
32769     /**
32770      * Returns the panel specified or null if it's not in this region.
32771      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32772      * @return {Roo.ContentPanel}
32773      */
32774     getPanel : function(id){
32775         if(typeof id == "object"){ // must be panel obj
32776             return id;
32777         }
32778         return this.panels.get(id);
32779     },
32780     
32781     /**
32782      * Returns this regions position (north/south/east/west/center).
32783      * @return {String} 
32784      */
32785     getPosition: function(){
32786         return this.position;    
32787     }
32788 });/*
32789  * Based on:
32790  * Ext JS Library 1.1.1
32791  * Copyright(c) 2006-2007, Ext JS, LLC.
32792  *
32793  * Originally Released Under LGPL - original licence link has changed is not relivant.
32794  *
32795  * Fork - LGPL
32796  * <script type="text/javascript">
32797  */
32798  
32799 /**
32800  * @class Roo.bootstrap.layout.Region
32801  * @extends Roo.bootstrap.layout.Basic
32802  * This class represents a region in a layout manager.
32803  
32804  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32805  * @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})
32806  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32807  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32808  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32809  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32810  * @cfg {String}    title           The title for the region (overrides panel titles)
32811  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32812  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32813  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32814  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32815  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32816  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32817  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32818  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32819  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32820  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32821
32822  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32823  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32824  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32825  * @cfg {Number}    width           For East/West panels
32826  * @cfg {Number}    height          For North/South panels
32827  * @cfg {Boolean}   split           To show the splitter
32828  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32829  * 
32830  * @cfg {string}   cls             Extra CSS classes to add to region
32831  * 
32832  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32833  * @cfg {string}   region  the region that it inhabits..
32834  *
32835
32836  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32837  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32838
32839  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32840  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32841  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32842  */
32843 Roo.bootstrap.layout.Region = function(config)
32844 {
32845     this.applyConfig(config);
32846
32847     var mgr = config.mgr;
32848     var pos = config.region;
32849     config.skipConfig = true;
32850     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32851     
32852     if (mgr.el) {
32853         this.onRender(mgr.el);   
32854     }
32855      
32856     this.visible = true;
32857     this.collapsed = false;
32858     this.unrendered_panels = [];
32859 };
32860
32861 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32862
32863     position: '', // set by wrapper (eg. north/south etc..)
32864     unrendered_panels : null,  // unrendered panels.
32865     createBody : function(){
32866         /** This region's body element 
32867         * @type Roo.Element */
32868         this.bodyEl = this.el.createChild({
32869                 tag: "div",
32870                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32871         });
32872     },
32873
32874     onRender: function(ctr, pos)
32875     {
32876         var dh = Roo.DomHelper;
32877         /** This region's container element 
32878         * @type Roo.Element */
32879         this.el = dh.append(ctr.dom, {
32880                 tag: "div",
32881                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32882             }, true);
32883         /** This region's title element 
32884         * @type Roo.Element */
32885     
32886         this.titleEl = dh.append(this.el.dom,
32887             {
32888                     tag: "div",
32889                     unselectable: "on",
32890                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32891                     children:[
32892                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32893                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32894                     ]}, true);
32895         
32896         this.titleEl.enableDisplayMode();
32897         /** This region's title text element 
32898         * @type HTMLElement */
32899         this.titleTextEl = this.titleEl.dom.firstChild;
32900         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32901         /*
32902         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32903         this.closeBtn.enableDisplayMode();
32904         this.closeBtn.on("click", this.closeClicked, this);
32905         this.closeBtn.hide();
32906     */
32907         this.createBody(this.config);
32908         if(this.config.hideWhenEmpty){
32909             this.hide();
32910             this.on("paneladded", this.validateVisibility, this);
32911             this.on("panelremoved", this.validateVisibility, this);
32912         }
32913         if(this.autoScroll){
32914             this.bodyEl.setStyle("overflow", "auto");
32915         }else{
32916             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32917         }
32918         //if(c.titlebar !== false){
32919             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32920                 this.titleEl.hide();
32921             }else{
32922                 this.titleEl.show();
32923                 if(this.config.title){
32924                     this.titleTextEl.innerHTML = this.config.title;
32925                 }
32926             }
32927         //}
32928         if(this.config.collapsed){
32929             this.collapse(true);
32930         }
32931         if(this.config.hidden){
32932             this.hide();
32933         }
32934         
32935         if (this.unrendered_panels && this.unrendered_panels.length) {
32936             for (var i =0;i< this.unrendered_panels.length; i++) {
32937                 this.add(this.unrendered_panels[i]);
32938             }
32939             this.unrendered_panels = null;
32940             
32941         }
32942         
32943     },
32944     
32945     applyConfig : function(c)
32946     {
32947         /*
32948          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32949             var dh = Roo.DomHelper;
32950             if(c.titlebar !== false){
32951                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32952                 this.collapseBtn.on("click", this.collapse, this);
32953                 this.collapseBtn.enableDisplayMode();
32954                 /*
32955                 if(c.showPin === true || this.showPin){
32956                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32957                     this.stickBtn.enableDisplayMode();
32958                     this.stickBtn.on("click", this.expand, this);
32959                     this.stickBtn.hide();
32960                 }
32961                 
32962             }
32963             */
32964             /** This region's collapsed element
32965             * @type Roo.Element */
32966             /*
32967              *
32968             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32969                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32970             ]}, true);
32971             
32972             if(c.floatable !== false){
32973                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32974                this.collapsedEl.on("click", this.collapseClick, this);
32975             }
32976
32977             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32978                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32979                    id: "message", unselectable: "on", style:{"float":"left"}});
32980                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32981              }
32982             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32983             this.expandBtn.on("click", this.expand, this);
32984             
32985         }
32986         
32987         if(this.collapseBtn){
32988             this.collapseBtn.setVisible(c.collapsible == true);
32989         }
32990         
32991         this.cmargins = c.cmargins || this.cmargins ||
32992                          (this.position == "west" || this.position == "east" ?
32993                              {top: 0, left: 2, right:2, bottom: 0} :
32994                              {top: 2, left: 0, right:0, bottom: 2});
32995         */
32996         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32997         
32998         
32999         this.bottomTabs = c.tabPosition != "top";
33000         
33001         this.autoScroll = c.autoScroll || false;
33002         
33003         
33004        
33005         
33006         this.duration = c.duration || .30;
33007         this.slideDuration = c.slideDuration || .45;
33008         this.config = c;
33009        
33010     },
33011     /**
33012      * Returns true if this region is currently visible.
33013      * @return {Boolean}
33014      */
33015     isVisible : function(){
33016         return this.visible;
33017     },
33018
33019     /**
33020      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33021      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33022      */
33023     //setCollapsedTitle : function(title){
33024     //    title = title || "&#160;";
33025      //   if(this.collapsedTitleTextEl){
33026       //      this.collapsedTitleTextEl.innerHTML = title;
33027        // }
33028     //},
33029
33030     getBox : function(){
33031         var b;
33032       //  if(!this.collapsed){
33033             b = this.el.getBox(false, true);
33034        // }else{
33035           //  b = this.collapsedEl.getBox(false, true);
33036         //}
33037         return b;
33038     },
33039
33040     getMargins : function(){
33041         return this.margins;
33042         //return this.collapsed ? this.cmargins : this.margins;
33043     },
33044 /*
33045     highlight : function(){
33046         this.el.addClass("x-layout-panel-dragover");
33047     },
33048
33049     unhighlight : function(){
33050         this.el.removeClass("x-layout-panel-dragover");
33051     },
33052 */
33053     updateBox : function(box)
33054     {
33055         if (!this.bodyEl) {
33056             return; // not rendered yet..
33057         }
33058         
33059         this.box = box;
33060         if(!this.collapsed){
33061             this.el.dom.style.left = box.x + "px";
33062             this.el.dom.style.top = box.y + "px";
33063             this.updateBody(box.width, box.height);
33064         }else{
33065             this.collapsedEl.dom.style.left = box.x + "px";
33066             this.collapsedEl.dom.style.top = box.y + "px";
33067             this.collapsedEl.setSize(box.width, box.height);
33068         }
33069         if(this.tabs){
33070             this.tabs.autoSizeTabs();
33071         }
33072     },
33073
33074     updateBody : function(w, h)
33075     {
33076         if(w !== null){
33077             this.el.setWidth(w);
33078             w -= this.el.getBorderWidth("rl");
33079             if(this.config.adjustments){
33080                 w += this.config.adjustments[0];
33081             }
33082         }
33083         if(h !== null && h > 0){
33084             this.el.setHeight(h);
33085             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33086             h -= this.el.getBorderWidth("tb");
33087             if(this.config.adjustments){
33088                 h += this.config.adjustments[1];
33089             }
33090             this.bodyEl.setHeight(h);
33091             if(this.tabs){
33092                 h = this.tabs.syncHeight(h);
33093             }
33094         }
33095         if(this.panelSize){
33096             w = w !== null ? w : this.panelSize.width;
33097             h = h !== null ? h : this.panelSize.height;
33098         }
33099         if(this.activePanel){
33100             var el = this.activePanel.getEl();
33101             w = w !== null ? w : el.getWidth();
33102             h = h !== null ? h : el.getHeight();
33103             this.panelSize = {width: w, height: h};
33104             this.activePanel.setSize(w, h);
33105         }
33106         if(Roo.isIE && this.tabs){
33107             this.tabs.el.repaint();
33108         }
33109     },
33110
33111     /**
33112      * Returns the container element for this region.
33113      * @return {Roo.Element}
33114      */
33115     getEl : function(){
33116         return this.el;
33117     },
33118
33119     /**
33120      * Hides this region.
33121      */
33122     hide : function(){
33123         //if(!this.collapsed){
33124             this.el.dom.style.left = "-2000px";
33125             this.el.hide();
33126         //}else{
33127          //   this.collapsedEl.dom.style.left = "-2000px";
33128          //   this.collapsedEl.hide();
33129        // }
33130         this.visible = false;
33131         this.fireEvent("visibilitychange", this, false);
33132     },
33133
33134     /**
33135      * Shows this region if it was previously hidden.
33136      */
33137     show : function(){
33138         //if(!this.collapsed){
33139             this.el.show();
33140         //}else{
33141         //    this.collapsedEl.show();
33142        // }
33143         this.visible = true;
33144         this.fireEvent("visibilitychange", this, true);
33145     },
33146 /*
33147     closeClicked : function(){
33148         if(this.activePanel){
33149             this.remove(this.activePanel);
33150         }
33151     },
33152
33153     collapseClick : function(e){
33154         if(this.isSlid){
33155            e.stopPropagation();
33156            this.slideIn();
33157         }else{
33158            e.stopPropagation();
33159            this.slideOut();
33160         }
33161     },
33162 */
33163     /**
33164      * Collapses this region.
33165      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33166      */
33167     /*
33168     collapse : function(skipAnim, skipCheck = false){
33169         if(this.collapsed) {
33170             return;
33171         }
33172         
33173         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33174             
33175             this.collapsed = true;
33176             if(this.split){
33177                 this.split.el.hide();
33178             }
33179             if(this.config.animate && skipAnim !== true){
33180                 this.fireEvent("invalidated", this);
33181                 this.animateCollapse();
33182             }else{
33183                 this.el.setLocation(-20000,-20000);
33184                 this.el.hide();
33185                 this.collapsedEl.show();
33186                 this.fireEvent("collapsed", this);
33187                 this.fireEvent("invalidated", this);
33188             }
33189         }
33190         
33191     },
33192 */
33193     animateCollapse : function(){
33194         // overridden
33195     },
33196
33197     /**
33198      * Expands this region if it was previously collapsed.
33199      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33200      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33201      */
33202     /*
33203     expand : function(e, skipAnim){
33204         if(e) {
33205             e.stopPropagation();
33206         }
33207         if(!this.collapsed || this.el.hasActiveFx()) {
33208             return;
33209         }
33210         if(this.isSlid){
33211             this.afterSlideIn();
33212             skipAnim = true;
33213         }
33214         this.collapsed = false;
33215         if(this.config.animate && skipAnim !== true){
33216             this.animateExpand();
33217         }else{
33218             this.el.show();
33219             if(this.split){
33220                 this.split.el.show();
33221             }
33222             this.collapsedEl.setLocation(-2000,-2000);
33223             this.collapsedEl.hide();
33224             this.fireEvent("invalidated", this);
33225             this.fireEvent("expanded", this);
33226         }
33227     },
33228 */
33229     animateExpand : function(){
33230         // overridden
33231     },
33232
33233     initTabs : function()
33234     {
33235         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33236         
33237         var ts = new Roo.bootstrap.panel.Tabs({
33238                 el: this.bodyEl.dom,
33239                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33240                 disableTooltips: this.config.disableTabTips,
33241                 toolbar : this.config.toolbar
33242             });
33243         
33244         if(this.config.hideTabs){
33245             ts.stripWrap.setDisplayed(false);
33246         }
33247         this.tabs = ts;
33248         ts.resizeTabs = this.config.resizeTabs === true;
33249         ts.minTabWidth = this.config.minTabWidth || 40;
33250         ts.maxTabWidth = this.config.maxTabWidth || 250;
33251         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33252         ts.monitorResize = false;
33253         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33254         ts.bodyEl.addClass('roo-layout-tabs-body');
33255         this.panels.each(this.initPanelAsTab, this);
33256     },
33257
33258     initPanelAsTab : function(panel){
33259         var ti = this.tabs.addTab(
33260                     panel.getEl().id,
33261                     panel.getTitle(),
33262                     null,
33263                     this.config.closeOnTab && panel.isClosable()
33264             );
33265         if(panel.tabTip !== undefined){
33266             ti.setTooltip(panel.tabTip);
33267         }
33268         ti.on("activate", function(){
33269               this.setActivePanel(panel);
33270         }, this);
33271         
33272         if(this.config.closeOnTab){
33273             ti.on("beforeclose", function(t, e){
33274                 e.cancel = true;
33275                 this.remove(panel);
33276             }, this);
33277         }
33278         return ti;
33279     },
33280
33281     updatePanelTitle : function(panel, title)
33282     {
33283         if(this.activePanel == panel){
33284             this.updateTitle(title);
33285         }
33286         if(this.tabs){
33287             var ti = this.tabs.getTab(panel.getEl().id);
33288             ti.setText(title);
33289             if(panel.tabTip !== undefined){
33290                 ti.setTooltip(panel.tabTip);
33291             }
33292         }
33293     },
33294
33295     updateTitle : function(title){
33296         if(this.titleTextEl && !this.config.title){
33297             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33298         }
33299     },
33300
33301     setActivePanel : function(panel)
33302     {
33303         panel = this.getPanel(panel);
33304         if(this.activePanel && this.activePanel != panel){
33305             this.activePanel.setActiveState(false);
33306         }
33307         this.activePanel = panel;
33308         panel.setActiveState(true);
33309         if(this.panelSize){
33310             panel.setSize(this.panelSize.width, this.panelSize.height);
33311         }
33312         if(this.closeBtn){
33313             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33314         }
33315         this.updateTitle(panel.getTitle());
33316         if(this.tabs){
33317             this.fireEvent("invalidated", this);
33318         }
33319         this.fireEvent("panelactivated", this, panel);
33320     },
33321
33322     /**
33323      * Shows the specified panel.
33324      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33325      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33326      */
33327     showPanel : function(panel)
33328     {
33329         panel = this.getPanel(panel);
33330         if(panel){
33331             if(this.tabs){
33332                 var tab = this.tabs.getTab(panel.getEl().id);
33333                 if(tab.isHidden()){
33334                     this.tabs.unhideTab(tab.id);
33335                 }
33336                 tab.activate();
33337             }else{
33338                 this.setActivePanel(panel);
33339             }
33340         }
33341         return panel;
33342     },
33343
33344     /**
33345      * Get the active panel for this region.
33346      * @return {Roo.ContentPanel} The active panel or null
33347      */
33348     getActivePanel : function(){
33349         return this.activePanel;
33350     },
33351
33352     validateVisibility : function(){
33353         if(this.panels.getCount() < 1){
33354             this.updateTitle("&#160;");
33355             this.closeBtn.hide();
33356             this.hide();
33357         }else{
33358             if(!this.isVisible()){
33359                 this.show();
33360             }
33361         }
33362     },
33363
33364     /**
33365      * Adds the passed ContentPanel(s) to this region.
33366      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33367      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33368      */
33369     add : function(panel)
33370     {
33371         if(arguments.length > 1){
33372             for(var i = 0, len = arguments.length; i < len; i++) {
33373                 this.add(arguments[i]);
33374             }
33375             return null;
33376         }
33377         
33378         // if we have not been rendered yet, then we can not really do much of this..
33379         if (!this.bodyEl) {
33380             this.unrendered_panels.push(panel);
33381             return panel;
33382         }
33383         
33384         
33385         
33386         
33387         if(this.hasPanel(panel)){
33388             this.showPanel(panel);
33389             return panel;
33390         }
33391         panel.setRegion(this);
33392         this.panels.add(panel);
33393        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33394             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33395             // and hide them... ???
33396             this.bodyEl.dom.appendChild(panel.getEl().dom);
33397             if(panel.background !== true){
33398                 this.setActivePanel(panel);
33399             }
33400             this.fireEvent("paneladded", this, panel);
33401             return panel;
33402         }
33403         */
33404         if(!this.tabs){
33405             this.initTabs();
33406         }else{
33407             this.initPanelAsTab(panel);
33408         }
33409         
33410         
33411         if(panel.background !== true){
33412             this.tabs.activate(panel.getEl().id);
33413         }
33414         this.fireEvent("paneladded", this, panel);
33415         return panel;
33416     },
33417
33418     /**
33419      * Hides the tab for the specified panel.
33420      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33421      */
33422     hidePanel : function(panel){
33423         if(this.tabs && (panel = this.getPanel(panel))){
33424             this.tabs.hideTab(panel.getEl().id);
33425         }
33426     },
33427
33428     /**
33429      * Unhides the tab for a previously hidden panel.
33430      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33431      */
33432     unhidePanel : function(panel){
33433         if(this.tabs && (panel = this.getPanel(panel))){
33434             this.tabs.unhideTab(panel.getEl().id);
33435         }
33436     },
33437
33438     clearPanels : function(){
33439         while(this.panels.getCount() > 0){
33440              this.remove(this.panels.first());
33441         }
33442     },
33443
33444     /**
33445      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33446      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33447      * @param {Boolean} preservePanel Overrides the config preservePanel option
33448      * @return {Roo.ContentPanel} The panel that was removed
33449      */
33450     remove : function(panel, preservePanel)
33451     {
33452         panel = this.getPanel(panel);
33453         if(!panel){
33454             return null;
33455         }
33456         var e = {};
33457         this.fireEvent("beforeremove", this, panel, e);
33458         if(e.cancel === true){
33459             return null;
33460         }
33461         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33462         var panelId = panel.getId();
33463         this.panels.removeKey(panelId);
33464         if(preservePanel){
33465             document.body.appendChild(panel.getEl().dom);
33466         }
33467         if(this.tabs){
33468             this.tabs.removeTab(panel.getEl().id);
33469         }else if (!preservePanel){
33470             this.bodyEl.dom.removeChild(panel.getEl().dom);
33471         }
33472         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33473             var p = this.panels.first();
33474             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33475             tempEl.appendChild(p.getEl().dom);
33476             this.bodyEl.update("");
33477             this.bodyEl.dom.appendChild(p.getEl().dom);
33478             tempEl = null;
33479             this.updateTitle(p.getTitle());
33480             this.tabs = null;
33481             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33482             this.setActivePanel(p);
33483         }
33484         panel.setRegion(null);
33485         if(this.activePanel == panel){
33486             this.activePanel = null;
33487         }
33488         if(this.config.autoDestroy !== false && preservePanel !== true){
33489             try{panel.destroy();}catch(e){}
33490         }
33491         this.fireEvent("panelremoved", this, panel);
33492         return panel;
33493     },
33494
33495     /**
33496      * Returns the TabPanel component used by this region
33497      * @return {Roo.TabPanel}
33498      */
33499     getTabs : function(){
33500         return this.tabs;
33501     },
33502
33503     createTool : function(parentEl, className){
33504         var btn = Roo.DomHelper.append(parentEl, {
33505             tag: "div",
33506             cls: "x-layout-tools-button",
33507             children: [ {
33508                 tag: "div",
33509                 cls: "roo-layout-tools-button-inner " + className,
33510                 html: "&#160;"
33511             }]
33512         }, true);
33513         btn.addClassOnOver("roo-layout-tools-button-over");
33514         return btn;
33515     }
33516 });/*
33517  * Based on:
33518  * Ext JS Library 1.1.1
33519  * Copyright(c) 2006-2007, Ext JS, LLC.
33520  *
33521  * Originally Released Under LGPL - original licence link has changed is not relivant.
33522  *
33523  * Fork - LGPL
33524  * <script type="text/javascript">
33525  */
33526  
33527
33528
33529 /**
33530  * @class Roo.SplitLayoutRegion
33531  * @extends Roo.LayoutRegion
33532  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33533  */
33534 Roo.bootstrap.layout.Split = function(config){
33535     this.cursor = config.cursor;
33536     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33537 };
33538
33539 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33540 {
33541     splitTip : "Drag to resize.",
33542     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33543     useSplitTips : false,
33544
33545     applyConfig : function(config){
33546         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33547     },
33548     
33549     onRender : function(ctr,pos) {
33550         
33551         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33552         if(!this.config.split){
33553             return;
33554         }
33555         if(!this.split){
33556             
33557             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33558                             tag: "div",
33559                             id: this.el.id + "-split",
33560                             cls: "roo-layout-split roo-layout-split-"+this.position,
33561                             html: "&#160;"
33562             });
33563             /** The SplitBar for this region 
33564             * @type Roo.SplitBar */
33565             // does not exist yet...
33566             Roo.log([this.position, this.orientation]);
33567             
33568             this.split = new Roo.bootstrap.SplitBar({
33569                 dragElement : splitEl,
33570                 resizingElement: this.el,
33571                 orientation : this.orientation
33572             });
33573             
33574             this.split.on("moved", this.onSplitMove, this);
33575             this.split.useShim = this.config.useShim === true;
33576             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33577             if(this.useSplitTips){
33578                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33579             }
33580             //if(config.collapsible){
33581             //    this.split.el.on("dblclick", this.collapse,  this);
33582             //}
33583         }
33584         if(typeof this.config.minSize != "undefined"){
33585             this.split.minSize = this.config.minSize;
33586         }
33587         if(typeof this.config.maxSize != "undefined"){
33588             this.split.maxSize = this.config.maxSize;
33589         }
33590         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33591             this.hideSplitter();
33592         }
33593         
33594     },
33595
33596     getHMaxSize : function(){
33597          var cmax = this.config.maxSize || 10000;
33598          var center = this.mgr.getRegion("center");
33599          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33600     },
33601
33602     getVMaxSize : function(){
33603          var cmax = this.config.maxSize || 10000;
33604          var center = this.mgr.getRegion("center");
33605          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33606     },
33607
33608     onSplitMove : function(split, newSize){
33609         this.fireEvent("resized", this, newSize);
33610     },
33611     
33612     /** 
33613      * Returns the {@link Roo.SplitBar} for this region.
33614      * @return {Roo.SplitBar}
33615      */
33616     getSplitBar : function(){
33617         return this.split;
33618     },
33619     
33620     hide : function(){
33621         this.hideSplitter();
33622         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33623     },
33624
33625     hideSplitter : function(){
33626         if(this.split){
33627             this.split.el.setLocation(-2000,-2000);
33628             this.split.el.hide();
33629         }
33630     },
33631
33632     show : function(){
33633         if(this.split){
33634             this.split.el.show();
33635         }
33636         Roo.bootstrap.layout.Split.superclass.show.call(this);
33637     },
33638     
33639     beforeSlide: function(){
33640         if(Roo.isGecko){// firefox overflow auto bug workaround
33641             this.bodyEl.clip();
33642             if(this.tabs) {
33643                 this.tabs.bodyEl.clip();
33644             }
33645             if(this.activePanel){
33646                 this.activePanel.getEl().clip();
33647                 
33648                 if(this.activePanel.beforeSlide){
33649                     this.activePanel.beforeSlide();
33650                 }
33651             }
33652         }
33653     },
33654     
33655     afterSlide : function(){
33656         if(Roo.isGecko){// firefox overflow auto bug workaround
33657             this.bodyEl.unclip();
33658             if(this.tabs) {
33659                 this.tabs.bodyEl.unclip();
33660             }
33661             if(this.activePanel){
33662                 this.activePanel.getEl().unclip();
33663                 if(this.activePanel.afterSlide){
33664                     this.activePanel.afterSlide();
33665                 }
33666             }
33667         }
33668     },
33669
33670     initAutoHide : function(){
33671         if(this.autoHide !== false){
33672             if(!this.autoHideHd){
33673                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33674                 this.autoHideHd = {
33675                     "mouseout": function(e){
33676                         if(!e.within(this.el, true)){
33677                             st.delay(500);
33678                         }
33679                     },
33680                     "mouseover" : function(e){
33681                         st.cancel();
33682                     },
33683                     scope : this
33684                 };
33685             }
33686             this.el.on(this.autoHideHd);
33687         }
33688     },
33689
33690     clearAutoHide : function(){
33691         if(this.autoHide !== false){
33692             this.el.un("mouseout", this.autoHideHd.mouseout);
33693             this.el.un("mouseover", this.autoHideHd.mouseover);
33694         }
33695     },
33696
33697     clearMonitor : function(){
33698         Roo.get(document).un("click", this.slideInIf, this);
33699     },
33700
33701     // these names are backwards but not changed for compat
33702     slideOut : function(){
33703         if(this.isSlid || this.el.hasActiveFx()){
33704             return;
33705         }
33706         this.isSlid = true;
33707         if(this.collapseBtn){
33708             this.collapseBtn.hide();
33709         }
33710         this.closeBtnState = this.closeBtn.getStyle('display');
33711         this.closeBtn.hide();
33712         if(this.stickBtn){
33713             this.stickBtn.show();
33714         }
33715         this.el.show();
33716         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33717         this.beforeSlide();
33718         this.el.setStyle("z-index", 10001);
33719         this.el.slideIn(this.getSlideAnchor(), {
33720             callback: function(){
33721                 this.afterSlide();
33722                 this.initAutoHide();
33723                 Roo.get(document).on("click", this.slideInIf, this);
33724                 this.fireEvent("slideshow", this);
33725             },
33726             scope: this,
33727             block: true
33728         });
33729     },
33730
33731     afterSlideIn : function(){
33732         this.clearAutoHide();
33733         this.isSlid = false;
33734         this.clearMonitor();
33735         this.el.setStyle("z-index", "");
33736         if(this.collapseBtn){
33737             this.collapseBtn.show();
33738         }
33739         this.closeBtn.setStyle('display', this.closeBtnState);
33740         if(this.stickBtn){
33741             this.stickBtn.hide();
33742         }
33743         this.fireEvent("slidehide", this);
33744     },
33745
33746     slideIn : function(cb){
33747         if(!this.isSlid || this.el.hasActiveFx()){
33748             Roo.callback(cb);
33749             return;
33750         }
33751         this.isSlid = false;
33752         this.beforeSlide();
33753         this.el.slideOut(this.getSlideAnchor(), {
33754             callback: function(){
33755                 this.el.setLeftTop(-10000, -10000);
33756                 this.afterSlide();
33757                 this.afterSlideIn();
33758                 Roo.callback(cb);
33759             },
33760             scope: this,
33761             block: true
33762         });
33763     },
33764     
33765     slideInIf : function(e){
33766         if(!e.within(this.el)){
33767             this.slideIn();
33768         }
33769     },
33770
33771     animateCollapse : function(){
33772         this.beforeSlide();
33773         this.el.setStyle("z-index", 20000);
33774         var anchor = this.getSlideAnchor();
33775         this.el.slideOut(anchor, {
33776             callback : function(){
33777                 this.el.setStyle("z-index", "");
33778                 this.collapsedEl.slideIn(anchor, {duration:.3});
33779                 this.afterSlide();
33780                 this.el.setLocation(-10000,-10000);
33781                 this.el.hide();
33782                 this.fireEvent("collapsed", this);
33783             },
33784             scope: this,
33785             block: true
33786         });
33787     },
33788
33789     animateExpand : function(){
33790         this.beforeSlide();
33791         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33792         this.el.setStyle("z-index", 20000);
33793         this.collapsedEl.hide({
33794             duration:.1
33795         });
33796         this.el.slideIn(this.getSlideAnchor(), {
33797             callback : function(){
33798                 this.el.setStyle("z-index", "");
33799                 this.afterSlide();
33800                 if(this.split){
33801                     this.split.el.show();
33802                 }
33803                 this.fireEvent("invalidated", this);
33804                 this.fireEvent("expanded", this);
33805             },
33806             scope: this,
33807             block: true
33808         });
33809     },
33810
33811     anchors : {
33812         "west" : "left",
33813         "east" : "right",
33814         "north" : "top",
33815         "south" : "bottom"
33816     },
33817
33818     sanchors : {
33819         "west" : "l",
33820         "east" : "r",
33821         "north" : "t",
33822         "south" : "b"
33823     },
33824
33825     canchors : {
33826         "west" : "tl-tr",
33827         "east" : "tr-tl",
33828         "north" : "tl-bl",
33829         "south" : "bl-tl"
33830     },
33831
33832     getAnchor : function(){
33833         return this.anchors[this.position];
33834     },
33835
33836     getCollapseAnchor : function(){
33837         return this.canchors[this.position];
33838     },
33839
33840     getSlideAnchor : function(){
33841         return this.sanchors[this.position];
33842     },
33843
33844     getAlignAdj : function(){
33845         var cm = this.cmargins;
33846         switch(this.position){
33847             case "west":
33848                 return [0, 0];
33849             break;
33850             case "east":
33851                 return [0, 0];
33852             break;
33853             case "north":
33854                 return [0, 0];
33855             break;
33856             case "south":
33857                 return [0, 0];
33858             break;
33859         }
33860     },
33861
33862     getExpandAdj : function(){
33863         var c = this.collapsedEl, cm = this.cmargins;
33864         switch(this.position){
33865             case "west":
33866                 return [-(cm.right+c.getWidth()+cm.left), 0];
33867             break;
33868             case "east":
33869                 return [cm.right+c.getWidth()+cm.left, 0];
33870             break;
33871             case "north":
33872                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33873             break;
33874             case "south":
33875                 return [0, cm.top+cm.bottom+c.getHeight()];
33876             break;
33877         }
33878     }
33879 });/*
33880  * Based on:
33881  * Ext JS Library 1.1.1
33882  * Copyright(c) 2006-2007, Ext JS, LLC.
33883  *
33884  * Originally Released Under LGPL - original licence link has changed is not relivant.
33885  *
33886  * Fork - LGPL
33887  * <script type="text/javascript">
33888  */
33889 /*
33890  * These classes are private internal classes
33891  */
33892 Roo.bootstrap.layout.Center = function(config){
33893     config.region = "center";
33894     Roo.bootstrap.layout.Region.call(this, config);
33895     this.visible = true;
33896     this.minWidth = config.minWidth || 20;
33897     this.minHeight = config.minHeight || 20;
33898 };
33899
33900 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33901     hide : function(){
33902         // center panel can't be hidden
33903     },
33904     
33905     show : function(){
33906         // center panel can't be hidden
33907     },
33908     
33909     getMinWidth: function(){
33910         return this.minWidth;
33911     },
33912     
33913     getMinHeight: function(){
33914         return this.minHeight;
33915     }
33916 });
33917
33918
33919
33920
33921  
33922
33923
33924
33925
33926
33927 Roo.bootstrap.layout.North = function(config)
33928 {
33929     config.region = 'north';
33930     config.cursor = 'n-resize';
33931     
33932     Roo.bootstrap.layout.Split.call(this, config);
33933     
33934     
33935     if(this.split){
33936         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33937         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33938         this.split.el.addClass("roo-layout-split-v");
33939     }
33940     var size = config.initialSize || config.height;
33941     if(typeof size != "undefined"){
33942         this.el.setHeight(size);
33943     }
33944 };
33945 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33946 {
33947     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33948     
33949     
33950     
33951     getBox : function(){
33952         if(this.collapsed){
33953             return this.collapsedEl.getBox();
33954         }
33955         var box = this.el.getBox();
33956         if(this.split){
33957             box.height += this.split.el.getHeight();
33958         }
33959         return box;
33960     },
33961     
33962     updateBox : function(box){
33963         if(this.split && !this.collapsed){
33964             box.height -= this.split.el.getHeight();
33965             this.split.el.setLeft(box.x);
33966             this.split.el.setTop(box.y+box.height);
33967             this.split.el.setWidth(box.width);
33968         }
33969         if(this.collapsed){
33970             this.updateBody(box.width, null);
33971         }
33972         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33973     }
33974 });
33975
33976
33977
33978
33979
33980 Roo.bootstrap.layout.South = function(config){
33981     config.region = 'south';
33982     config.cursor = 's-resize';
33983     Roo.bootstrap.layout.Split.call(this, config);
33984     if(this.split){
33985         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33986         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33987         this.split.el.addClass("roo-layout-split-v");
33988     }
33989     var size = config.initialSize || config.height;
33990     if(typeof size != "undefined"){
33991         this.el.setHeight(size);
33992     }
33993 };
33994
33995 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33996     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33997     getBox : function(){
33998         if(this.collapsed){
33999             return this.collapsedEl.getBox();
34000         }
34001         var box = this.el.getBox();
34002         if(this.split){
34003             var sh = this.split.el.getHeight();
34004             box.height += sh;
34005             box.y -= sh;
34006         }
34007         return box;
34008     },
34009     
34010     updateBox : function(box){
34011         if(this.split && !this.collapsed){
34012             var sh = this.split.el.getHeight();
34013             box.height -= sh;
34014             box.y += sh;
34015             this.split.el.setLeft(box.x);
34016             this.split.el.setTop(box.y-sh);
34017             this.split.el.setWidth(box.width);
34018         }
34019         if(this.collapsed){
34020             this.updateBody(box.width, null);
34021         }
34022         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34023     }
34024 });
34025
34026 Roo.bootstrap.layout.East = function(config){
34027     config.region = "east";
34028     config.cursor = "e-resize";
34029     Roo.bootstrap.layout.Split.call(this, config);
34030     if(this.split){
34031         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34032         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34033         this.split.el.addClass("roo-layout-split-h");
34034     }
34035     var size = config.initialSize || config.width;
34036     if(typeof size != "undefined"){
34037         this.el.setWidth(size);
34038     }
34039 };
34040 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34041     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34042     getBox : function(){
34043         if(this.collapsed){
34044             return this.collapsedEl.getBox();
34045         }
34046         var box = this.el.getBox();
34047         if(this.split){
34048             var sw = this.split.el.getWidth();
34049             box.width += sw;
34050             box.x -= sw;
34051         }
34052         return box;
34053     },
34054
34055     updateBox : function(box){
34056         if(this.split && !this.collapsed){
34057             var sw = this.split.el.getWidth();
34058             box.width -= sw;
34059             this.split.el.setLeft(box.x);
34060             this.split.el.setTop(box.y);
34061             this.split.el.setHeight(box.height);
34062             box.x += sw;
34063         }
34064         if(this.collapsed){
34065             this.updateBody(null, box.height);
34066         }
34067         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34068     }
34069 });
34070
34071 Roo.bootstrap.layout.West = function(config){
34072     config.region = "west";
34073     config.cursor = "w-resize";
34074     
34075     Roo.bootstrap.layout.Split.call(this, config);
34076     if(this.split){
34077         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34078         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34079         this.split.el.addClass("roo-layout-split-h");
34080     }
34081     
34082 };
34083 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34084     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34085     
34086     onRender: function(ctr, pos)
34087     {
34088         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34089         var size = this.config.initialSize || this.config.width;
34090         if(typeof size != "undefined"){
34091             this.el.setWidth(size);
34092         }
34093     },
34094     
34095     getBox : function(){
34096         if(this.collapsed){
34097             return this.collapsedEl.getBox();
34098         }
34099         var box = this.el.getBox();
34100         if(this.split){
34101             box.width += this.split.el.getWidth();
34102         }
34103         return box;
34104     },
34105     
34106     updateBox : function(box){
34107         if(this.split && !this.collapsed){
34108             var sw = this.split.el.getWidth();
34109             box.width -= sw;
34110             this.split.el.setLeft(box.x+box.width);
34111             this.split.el.setTop(box.y);
34112             this.split.el.setHeight(box.height);
34113         }
34114         if(this.collapsed){
34115             this.updateBody(null, box.height);
34116         }
34117         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34118     }
34119 });
34120 Roo.namespace("Roo.bootstrap.panel");/*
34121  * Based on:
34122  * Ext JS Library 1.1.1
34123  * Copyright(c) 2006-2007, Ext JS, LLC.
34124  *
34125  * Originally Released Under LGPL - original licence link has changed is not relivant.
34126  *
34127  * Fork - LGPL
34128  * <script type="text/javascript">
34129  */
34130 /**
34131  * @class Roo.ContentPanel
34132  * @extends Roo.util.Observable
34133  * A basic ContentPanel element.
34134  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34135  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34136  * @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
34137  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34138  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34139  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34140  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34141  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34142  * @cfg {String} title          The title for this panel
34143  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34144  * @cfg {String} url            Calls {@link #setUrl} with this value
34145  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34146  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34147  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34148  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34149
34150  * @constructor
34151  * Create a new ContentPanel.
34152  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34153  * @param {String/Object} config A string to set only the title or a config object
34154  * @param {String} content (optional) Set the HTML content for this panel
34155  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34156  */
34157 Roo.bootstrap.panel.Content = function( config){
34158     
34159     var el = config.el;
34160     var content = config.content;
34161
34162     if(config.autoCreate){ // xtype is available if this is called from factory
34163         el = Roo.id();
34164     }
34165     this.el = Roo.get(el);
34166     if(!this.el && config && config.autoCreate){
34167         if(typeof config.autoCreate == "object"){
34168             if(!config.autoCreate.id){
34169                 config.autoCreate.id = config.id||el;
34170             }
34171             this.el = Roo.DomHelper.append(document.body,
34172                         config.autoCreate, true);
34173         }else{
34174             var elcfg =  {   tag: "div",
34175                             cls: "roo-layout-inactive-content",
34176                             id: config.id||el
34177                             };
34178             if (config.html) {
34179                 elcfg.html = config.html;
34180                 
34181             }
34182                         
34183             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34184         }
34185     } 
34186     this.closable = false;
34187     this.loaded = false;
34188     this.active = false;
34189    
34190       
34191     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34192         
34193         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34194         
34195         this.wrapEl = this.el.wrap();
34196         var ti = [];
34197         if (config.toolbar.items) {
34198             ti = config.toolbar.items ;
34199             delete config.toolbar.items ;
34200         }
34201         
34202         var nitems = [];
34203         this.toolbar.render(this.wrapEl, 'before');
34204         for(var i =0;i < ti.length;i++) {
34205           //  Roo.log(['add child', items[i]]);
34206             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34207         }
34208         this.toolbar.items = nitems;
34209         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34210         delete config.toolbar;
34211         
34212     }
34213     /*
34214     // xtype created footer. - not sure if will work as we normally have to render first..
34215     if (this.footer && !this.footer.el && this.footer.xtype) {
34216         if (!this.wrapEl) {
34217             this.wrapEl = this.el.wrap();
34218         }
34219     
34220         this.footer.container = this.wrapEl.createChild();
34221          
34222         this.footer = Roo.factory(this.footer, Roo);
34223         
34224     }
34225     */
34226     
34227      if(typeof config == "string"){
34228         this.title = config;
34229     }else{
34230         Roo.apply(this, config);
34231     }
34232     
34233     if(this.resizeEl){
34234         this.resizeEl = Roo.get(this.resizeEl, true);
34235     }else{
34236         this.resizeEl = this.el;
34237     }
34238     // handle view.xtype
34239     
34240  
34241     
34242     
34243     this.addEvents({
34244         /**
34245          * @event activate
34246          * Fires when this panel is activated. 
34247          * @param {Roo.ContentPanel} this
34248          */
34249         "activate" : true,
34250         /**
34251          * @event deactivate
34252          * Fires when this panel is activated. 
34253          * @param {Roo.ContentPanel} this
34254          */
34255         "deactivate" : true,
34256
34257         /**
34258          * @event resize
34259          * Fires when this panel is resized if fitToFrame is true.
34260          * @param {Roo.ContentPanel} this
34261          * @param {Number} width The width after any component adjustments
34262          * @param {Number} height The height after any component adjustments
34263          */
34264         "resize" : true,
34265         
34266          /**
34267          * @event render
34268          * Fires when this tab is created
34269          * @param {Roo.ContentPanel} this
34270          */
34271         "render" : true
34272         
34273         
34274         
34275     });
34276     
34277
34278     
34279     
34280     if(this.autoScroll){
34281         this.resizeEl.setStyle("overflow", "auto");
34282     } else {
34283         // fix randome scrolling
34284         //this.el.on('scroll', function() {
34285         //    Roo.log('fix random scolling');
34286         //    this.scrollTo('top',0); 
34287         //});
34288     }
34289     content = content || this.content;
34290     if(content){
34291         this.setContent(content);
34292     }
34293     if(config && config.url){
34294         this.setUrl(this.url, this.params, this.loadOnce);
34295     }
34296     
34297     
34298     
34299     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34300     
34301     if (this.view && typeof(this.view.xtype) != 'undefined') {
34302         this.view.el = this.el.appendChild(document.createElement("div"));
34303         this.view = Roo.factory(this.view); 
34304         this.view.render  &&  this.view.render(false, '');  
34305     }
34306     
34307     
34308     this.fireEvent('render', this);
34309 };
34310
34311 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34312     tabTip:'',
34313     setRegion : function(region){
34314         this.region = region;
34315         this.setActiveClass(region && !this.background);
34316     },
34317     
34318     
34319     setActiveClass: function(state)
34320     {
34321         if(state){
34322            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34323            this.el.setStyle('position','relative');
34324         }else{
34325            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34326            this.el.setStyle('position', 'absolute');
34327         } 
34328     },
34329     
34330     /**
34331      * Returns the toolbar for this Panel if one was configured. 
34332      * @return {Roo.Toolbar} 
34333      */
34334     getToolbar : function(){
34335         return this.toolbar;
34336     },
34337     
34338     setActiveState : function(active)
34339     {
34340         this.active = active;
34341         this.setActiveClass(active);
34342         if(!active){
34343             this.fireEvent("deactivate", this);
34344         }else{
34345             this.fireEvent("activate", this);
34346         }
34347     },
34348     /**
34349      * Updates this panel's element
34350      * @param {String} content The new content
34351      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34352     */
34353     setContent : function(content, loadScripts){
34354         this.el.update(content, loadScripts);
34355     },
34356
34357     ignoreResize : function(w, h){
34358         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34359             return true;
34360         }else{
34361             this.lastSize = {width: w, height: h};
34362             return false;
34363         }
34364     },
34365     /**
34366      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34367      * @return {Roo.UpdateManager} The UpdateManager
34368      */
34369     getUpdateManager : function(){
34370         return this.el.getUpdateManager();
34371     },
34372      /**
34373      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34374      * @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:
34375 <pre><code>
34376 panel.load({
34377     url: "your-url.php",
34378     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34379     callback: yourFunction,
34380     scope: yourObject, //(optional scope)
34381     discardUrl: false,
34382     nocache: false,
34383     text: "Loading...",
34384     timeout: 30,
34385     scripts: false
34386 });
34387 </code></pre>
34388      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34389      * 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.
34390      * @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}
34391      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34392      * @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.
34393      * @return {Roo.ContentPanel} this
34394      */
34395     load : function(){
34396         var um = this.el.getUpdateManager();
34397         um.update.apply(um, arguments);
34398         return this;
34399     },
34400
34401
34402     /**
34403      * 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.
34404      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34405      * @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)
34406      * @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)
34407      * @return {Roo.UpdateManager} The UpdateManager
34408      */
34409     setUrl : function(url, params, loadOnce){
34410         if(this.refreshDelegate){
34411             this.removeListener("activate", this.refreshDelegate);
34412         }
34413         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34414         this.on("activate", this.refreshDelegate);
34415         return this.el.getUpdateManager();
34416     },
34417     
34418     _handleRefresh : function(url, params, loadOnce){
34419         if(!loadOnce || !this.loaded){
34420             var updater = this.el.getUpdateManager();
34421             updater.update(url, params, this._setLoaded.createDelegate(this));
34422         }
34423     },
34424     
34425     _setLoaded : function(){
34426         this.loaded = true;
34427     }, 
34428     
34429     /**
34430      * Returns this panel's id
34431      * @return {String} 
34432      */
34433     getId : function(){
34434         return this.el.id;
34435     },
34436     
34437     /** 
34438      * Returns this panel's element - used by regiosn to add.
34439      * @return {Roo.Element} 
34440      */
34441     getEl : function(){
34442         return this.wrapEl || this.el;
34443     },
34444     
34445    
34446     
34447     adjustForComponents : function(width, height)
34448     {
34449         //Roo.log('adjustForComponents ');
34450         if(this.resizeEl != this.el){
34451             width -= this.el.getFrameWidth('lr');
34452             height -= this.el.getFrameWidth('tb');
34453         }
34454         if(this.toolbar){
34455             var te = this.toolbar.getEl();
34456             height -= te.getHeight();
34457             te.setWidth(width);
34458         }
34459         if(this.footer){
34460             var te = this.footer.getEl();
34461             Roo.log("footer:" + te.getHeight());
34462             
34463             height -= te.getHeight();
34464             te.setWidth(width);
34465         }
34466         
34467         
34468         if(this.adjustments){
34469             width += this.adjustments[0];
34470             height += this.adjustments[1];
34471         }
34472         return {"width": width, "height": height};
34473     },
34474     
34475     setSize : function(width, height){
34476         if(this.fitToFrame && !this.ignoreResize(width, height)){
34477             if(this.fitContainer && this.resizeEl != this.el){
34478                 this.el.setSize(width, height);
34479             }
34480             var size = this.adjustForComponents(width, height);
34481             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34482             this.fireEvent('resize', this, size.width, size.height);
34483         }
34484     },
34485     
34486     /**
34487      * Returns this panel's title
34488      * @return {String} 
34489      */
34490     getTitle : function(){
34491         return this.title;
34492     },
34493     
34494     /**
34495      * Set this panel's title
34496      * @param {String} title
34497      */
34498     setTitle : function(title){
34499         this.title = title;
34500         if(this.region){
34501             this.region.updatePanelTitle(this, title);
34502         }
34503     },
34504     
34505     /**
34506      * Returns true is this panel was configured to be closable
34507      * @return {Boolean} 
34508      */
34509     isClosable : function(){
34510         return this.closable;
34511     },
34512     
34513     beforeSlide : function(){
34514         this.el.clip();
34515         this.resizeEl.clip();
34516     },
34517     
34518     afterSlide : function(){
34519         this.el.unclip();
34520         this.resizeEl.unclip();
34521     },
34522     
34523     /**
34524      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34525      *   Will fail silently if the {@link #setUrl} method has not been called.
34526      *   This does not activate the panel, just updates its content.
34527      */
34528     refresh : function(){
34529         if(this.refreshDelegate){
34530            this.loaded = false;
34531            this.refreshDelegate();
34532         }
34533     },
34534     
34535     /**
34536      * Destroys this panel
34537      */
34538     destroy : function(){
34539         this.el.removeAllListeners();
34540         var tempEl = document.createElement("span");
34541         tempEl.appendChild(this.el.dom);
34542         tempEl.innerHTML = "";
34543         this.el.remove();
34544         this.el = null;
34545     },
34546     
34547     /**
34548      * form - if the content panel contains a form - this is a reference to it.
34549      * @type {Roo.form.Form}
34550      */
34551     form : false,
34552     /**
34553      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34554      *    This contains a reference to it.
34555      * @type {Roo.View}
34556      */
34557     view : false,
34558     
34559       /**
34560      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34561      * <pre><code>
34562
34563 layout.addxtype({
34564        xtype : 'Form',
34565        items: [ .... ]
34566    }
34567 );
34568
34569 </code></pre>
34570      * @param {Object} cfg Xtype definition of item to add.
34571      */
34572     
34573     
34574     getChildContainer: function () {
34575         return this.getEl();
34576     }
34577     
34578     
34579     /*
34580         var  ret = new Roo.factory(cfg);
34581         return ret;
34582         
34583         
34584         // add form..
34585         if (cfg.xtype.match(/^Form$/)) {
34586             
34587             var el;
34588             //if (this.footer) {
34589             //    el = this.footer.container.insertSibling(false, 'before');
34590             //} else {
34591                 el = this.el.createChild();
34592             //}
34593
34594             this.form = new  Roo.form.Form(cfg);
34595             
34596             
34597             if ( this.form.allItems.length) {
34598                 this.form.render(el.dom);
34599             }
34600             return this.form;
34601         }
34602         // should only have one of theses..
34603         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34604             // views.. should not be just added - used named prop 'view''
34605             
34606             cfg.el = this.el.appendChild(document.createElement("div"));
34607             // factory?
34608             
34609             var ret = new Roo.factory(cfg);
34610              
34611              ret.render && ret.render(false, ''); // render blank..
34612             this.view = ret;
34613             return ret;
34614         }
34615         return false;
34616     }
34617     \*/
34618 });
34619  
34620 /**
34621  * @class Roo.bootstrap.panel.Grid
34622  * @extends Roo.bootstrap.panel.Content
34623  * @constructor
34624  * Create a new GridPanel.
34625  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34626  * @param {Object} config A the config object
34627   
34628  */
34629
34630
34631
34632 Roo.bootstrap.panel.Grid = function(config)
34633 {
34634     
34635       
34636     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34637         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34638
34639     config.el = this.wrapper;
34640     //this.el = this.wrapper;
34641     
34642       if (config.container) {
34643         // ctor'ed from a Border/panel.grid
34644         
34645         
34646         this.wrapper.setStyle("overflow", "hidden");
34647         this.wrapper.addClass('roo-grid-container');
34648
34649     }
34650     
34651     
34652     if(config.toolbar){
34653         var tool_el = this.wrapper.createChild();    
34654         this.toolbar = Roo.factory(config.toolbar);
34655         var ti = [];
34656         if (config.toolbar.items) {
34657             ti = config.toolbar.items ;
34658             delete config.toolbar.items ;
34659         }
34660         
34661         var nitems = [];
34662         this.toolbar.render(tool_el);
34663         for(var i =0;i < ti.length;i++) {
34664           //  Roo.log(['add child', items[i]]);
34665             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34666         }
34667         this.toolbar.items = nitems;
34668         
34669         delete config.toolbar;
34670     }
34671     
34672     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34673     config.grid.scrollBody = true;;
34674     config.grid.monitorWindowResize = false; // turn off autosizing
34675     config.grid.autoHeight = false;
34676     config.grid.autoWidth = false;
34677     
34678     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34679     
34680     if (config.background) {
34681         // render grid on panel activation (if panel background)
34682         this.on('activate', function(gp) {
34683             if (!gp.grid.rendered) {
34684                 gp.grid.render(el);
34685                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34686
34687             }
34688         });
34689             
34690     } else {
34691         this.grid.render(this.wrapper);
34692         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34693
34694     }
34695     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34696     // ??? needed ??? config.el = this.wrapper;
34697     
34698     
34699     
34700   
34701     // xtype created footer. - not sure if will work as we normally have to render first..
34702     if (this.footer && !this.footer.el && this.footer.xtype) {
34703         
34704         var ctr = this.grid.getView().getFooterPanel(true);
34705         this.footer.dataSource = this.grid.dataSource;
34706         this.footer = Roo.factory(this.footer, Roo);
34707         this.footer.render(ctr);
34708         
34709     }
34710     
34711     
34712     
34713     
34714      
34715 };
34716
34717 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34718     getId : function(){
34719         return this.grid.id;
34720     },
34721     
34722     /**
34723      * Returns the grid for this panel
34724      * @return {Roo.bootstrap.Table} 
34725      */
34726     getGrid : function(){
34727         return this.grid;    
34728     },
34729     
34730     setSize : function(width, height){
34731         if(!this.ignoreResize(width, height)){
34732             var grid = this.grid;
34733             var size = this.adjustForComponents(width, height);
34734             var gridel = grid.getGridEl();
34735             gridel.setSize(size.width, size.height);
34736             /*
34737             var thd = grid.getGridEl().select('thead',true).first();
34738             var tbd = grid.getGridEl().select('tbody', true).first();
34739             if (tbd) {
34740                 tbd.setSize(width, height - thd.getHeight());
34741             }
34742             */
34743             grid.autoSize();
34744         }
34745     },
34746      
34747     
34748     
34749     beforeSlide : function(){
34750         this.grid.getView().scroller.clip();
34751     },
34752     
34753     afterSlide : function(){
34754         this.grid.getView().scroller.unclip();
34755     },
34756     
34757     destroy : function(){
34758         this.grid.destroy();
34759         delete this.grid;
34760         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34761     }
34762 });
34763
34764 /**
34765  * @class Roo.bootstrap.panel.Nest
34766  * @extends Roo.bootstrap.panel.Content
34767  * @constructor
34768  * Create a new Panel, that can contain a layout.Border.
34769  * 
34770  * 
34771  * @param {Roo.BorderLayout} layout The layout for this panel
34772  * @param {String/Object} config A string to set only the title or a config object
34773  */
34774 Roo.bootstrap.panel.Nest = function(config)
34775 {
34776     // construct with only one argument..
34777     /* FIXME - implement nicer consturctors
34778     if (layout.layout) {
34779         config = layout;
34780         layout = config.layout;
34781         delete config.layout;
34782     }
34783     if (layout.xtype && !layout.getEl) {
34784         // then layout needs constructing..
34785         layout = Roo.factory(layout, Roo);
34786     }
34787     */
34788     
34789     config.el =  config.layout.getEl();
34790     
34791     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34792     
34793     config.layout.monitorWindowResize = false; // turn off autosizing
34794     this.layout = config.layout;
34795     this.layout.getEl().addClass("roo-layout-nested-layout");
34796     
34797     
34798     
34799     
34800 };
34801
34802 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34803
34804     setSize : function(width, height){
34805         if(!this.ignoreResize(width, height)){
34806             var size = this.adjustForComponents(width, height);
34807             var el = this.layout.getEl();
34808             if (size.height < 1) {
34809                 el.setWidth(size.width);   
34810             } else {
34811                 el.setSize(size.width, size.height);
34812             }
34813             var touch = el.dom.offsetWidth;
34814             this.layout.layout();
34815             // ie requires a double layout on the first pass
34816             if(Roo.isIE && !this.initialized){
34817                 this.initialized = true;
34818                 this.layout.layout();
34819             }
34820         }
34821     },
34822     
34823     // activate all subpanels if not currently active..
34824     
34825     setActiveState : function(active){
34826         this.active = active;
34827         this.setActiveClass(active);
34828         
34829         if(!active){
34830             this.fireEvent("deactivate", this);
34831             return;
34832         }
34833         
34834         this.fireEvent("activate", this);
34835         // not sure if this should happen before or after..
34836         if (!this.layout) {
34837             return; // should not happen..
34838         }
34839         var reg = false;
34840         for (var r in this.layout.regions) {
34841             reg = this.layout.getRegion(r);
34842             if (reg.getActivePanel()) {
34843                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34844                 reg.setActivePanel(reg.getActivePanel());
34845                 continue;
34846             }
34847             if (!reg.panels.length) {
34848                 continue;
34849             }
34850             reg.showPanel(reg.getPanel(0));
34851         }
34852         
34853         
34854         
34855         
34856     },
34857     
34858     /**
34859      * Returns the nested BorderLayout for this panel
34860      * @return {Roo.BorderLayout} 
34861      */
34862     getLayout : function(){
34863         return this.layout;
34864     },
34865     
34866      /**
34867      * Adds a xtype elements to the layout of the nested panel
34868      * <pre><code>
34869
34870 panel.addxtype({
34871        xtype : 'ContentPanel',
34872        region: 'west',
34873        items: [ .... ]
34874    }
34875 );
34876
34877 panel.addxtype({
34878         xtype : 'NestedLayoutPanel',
34879         region: 'west',
34880         layout: {
34881            center: { },
34882            west: { }   
34883         },
34884         items : [ ... list of content panels or nested layout panels.. ]
34885    }
34886 );
34887 </code></pre>
34888      * @param {Object} cfg Xtype definition of item to add.
34889      */
34890     addxtype : function(cfg) {
34891         return this.layout.addxtype(cfg);
34892     
34893     }
34894 });        /*
34895  * Based on:
34896  * Ext JS Library 1.1.1
34897  * Copyright(c) 2006-2007, Ext JS, LLC.
34898  *
34899  * Originally Released Under LGPL - original licence link has changed is not relivant.
34900  *
34901  * Fork - LGPL
34902  * <script type="text/javascript">
34903  */
34904 /**
34905  * @class Roo.TabPanel
34906  * @extends Roo.util.Observable
34907  * A lightweight tab container.
34908  * <br><br>
34909  * Usage:
34910  * <pre><code>
34911 // basic tabs 1, built from existing content
34912 var tabs = new Roo.TabPanel("tabs1");
34913 tabs.addTab("script", "View Script");
34914 tabs.addTab("markup", "View Markup");
34915 tabs.activate("script");
34916
34917 // more advanced tabs, built from javascript
34918 var jtabs = new Roo.TabPanel("jtabs");
34919 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34920
34921 // set up the UpdateManager
34922 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34923 var updater = tab2.getUpdateManager();
34924 updater.setDefaultUrl("ajax1.htm");
34925 tab2.on('activate', updater.refresh, updater, true);
34926
34927 // Use setUrl for Ajax loading
34928 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34929 tab3.setUrl("ajax2.htm", null, true);
34930
34931 // Disabled tab
34932 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34933 tab4.disable();
34934
34935 jtabs.activate("jtabs-1");
34936  * </code></pre>
34937  * @constructor
34938  * Create a new TabPanel.
34939  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34940  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34941  */
34942 Roo.bootstrap.panel.Tabs = function(config){
34943     /**
34944     * The container element for this TabPanel.
34945     * @type Roo.Element
34946     */
34947     this.el = Roo.get(config.el);
34948     delete config.el;
34949     if(config){
34950         if(typeof config == "boolean"){
34951             this.tabPosition = config ? "bottom" : "top";
34952         }else{
34953             Roo.apply(this, config);
34954         }
34955     }
34956     
34957     if(this.tabPosition == "bottom"){
34958         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34959         this.el.addClass("roo-tabs-bottom");
34960     }
34961     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34962     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34963     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34964     if(Roo.isIE){
34965         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34966     }
34967     if(this.tabPosition != "bottom"){
34968         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34969          * @type Roo.Element
34970          */
34971         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34972         this.el.addClass("roo-tabs-top");
34973     }
34974     this.items = [];
34975
34976     this.bodyEl.setStyle("position", "relative");
34977
34978     this.active = null;
34979     this.activateDelegate = this.activate.createDelegate(this);
34980
34981     this.addEvents({
34982         /**
34983          * @event tabchange
34984          * Fires when the active tab changes
34985          * @param {Roo.TabPanel} this
34986          * @param {Roo.TabPanelItem} activePanel The new active tab
34987          */
34988         "tabchange": true,
34989         /**
34990          * @event beforetabchange
34991          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34992          * @param {Roo.TabPanel} this
34993          * @param {Object} e Set cancel to true on this object to cancel the tab change
34994          * @param {Roo.TabPanelItem} tab The tab being changed to
34995          */
34996         "beforetabchange" : true
34997     });
34998
34999     Roo.EventManager.onWindowResize(this.onResize, this);
35000     this.cpad = this.el.getPadding("lr");
35001     this.hiddenCount = 0;
35002
35003
35004     // toolbar on the tabbar support...
35005     if (this.toolbar) {
35006         alert("no toolbar support yet");
35007         this.toolbar  = false;
35008         /*
35009         var tcfg = this.toolbar;
35010         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35011         this.toolbar = new Roo.Toolbar(tcfg);
35012         if (Roo.isSafari) {
35013             var tbl = tcfg.container.child('table', true);
35014             tbl.setAttribute('width', '100%');
35015         }
35016         */
35017         
35018     }
35019    
35020
35021
35022     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35023 };
35024
35025 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35026     /*
35027      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35028      */
35029     tabPosition : "top",
35030     /*
35031      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35032      */
35033     currentTabWidth : 0,
35034     /*
35035      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35036      */
35037     minTabWidth : 40,
35038     /*
35039      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35040      */
35041     maxTabWidth : 250,
35042     /*
35043      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35044      */
35045     preferredTabWidth : 175,
35046     /*
35047      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35048      */
35049     resizeTabs : false,
35050     /*
35051      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35052      */
35053     monitorResize : true,
35054     /*
35055      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35056      */
35057     toolbar : false,
35058
35059     /**
35060      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35061      * @param {String} id The id of the div to use <b>or create</b>
35062      * @param {String} text The text for the tab
35063      * @param {String} content (optional) Content to put in the TabPanelItem body
35064      * @param {Boolean} closable (optional) True to create a close icon on the tab
35065      * @return {Roo.TabPanelItem} The created TabPanelItem
35066      */
35067     addTab : function(id, text, content, closable)
35068     {
35069         var item = new Roo.bootstrap.panel.TabItem({
35070             panel: this,
35071             id : id,
35072             text : text,
35073             closable : closable
35074         });
35075         this.addTabItem(item);
35076         if(content){
35077             item.setContent(content);
35078         }
35079         return item;
35080     },
35081
35082     /**
35083      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35084      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35085      * @return {Roo.TabPanelItem}
35086      */
35087     getTab : function(id){
35088         return this.items[id];
35089     },
35090
35091     /**
35092      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35093      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35094      */
35095     hideTab : function(id){
35096         var t = this.items[id];
35097         if(!t.isHidden()){
35098            t.setHidden(true);
35099            this.hiddenCount++;
35100            this.autoSizeTabs();
35101         }
35102     },
35103
35104     /**
35105      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35106      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35107      */
35108     unhideTab : function(id){
35109         var t = this.items[id];
35110         if(t.isHidden()){
35111            t.setHidden(false);
35112            this.hiddenCount--;
35113            this.autoSizeTabs();
35114         }
35115     },
35116
35117     /**
35118      * Adds an existing {@link Roo.TabPanelItem}.
35119      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35120      */
35121     addTabItem : function(item){
35122         this.items[item.id] = item;
35123         this.items.push(item);
35124       //  if(this.resizeTabs){
35125     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35126   //         this.autoSizeTabs();
35127 //        }else{
35128 //            item.autoSize();
35129        // }
35130     },
35131
35132     /**
35133      * Removes a {@link Roo.TabPanelItem}.
35134      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35135      */
35136     removeTab : function(id){
35137         var items = this.items;
35138         var tab = items[id];
35139         if(!tab) { return; }
35140         var index = items.indexOf(tab);
35141         if(this.active == tab && items.length > 1){
35142             var newTab = this.getNextAvailable(index);
35143             if(newTab) {
35144                 newTab.activate();
35145             }
35146         }
35147         this.stripEl.dom.removeChild(tab.pnode.dom);
35148         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35149             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35150         }
35151         items.splice(index, 1);
35152         delete this.items[tab.id];
35153         tab.fireEvent("close", tab);
35154         tab.purgeListeners();
35155         this.autoSizeTabs();
35156     },
35157
35158     getNextAvailable : function(start){
35159         var items = this.items;
35160         var index = start;
35161         // look for a next tab that will slide over to
35162         // replace the one being removed
35163         while(index < items.length){
35164             var item = items[++index];
35165             if(item && !item.isHidden()){
35166                 return item;
35167             }
35168         }
35169         // if one isn't found select the previous tab (on the left)
35170         index = start;
35171         while(index >= 0){
35172             var item = items[--index];
35173             if(item && !item.isHidden()){
35174                 return item;
35175             }
35176         }
35177         return null;
35178     },
35179
35180     /**
35181      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35182      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35183      */
35184     disableTab : function(id){
35185         var tab = this.items[id];
35186         if(tab && this.active != tab){
35187             tab.disable();
35188         }
35189     },
35190
35191     /**
35192      * Enables a {@link Roo.TabPanelItem} that is disabled.
35193      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35194      */
35195     enableTab : function(id){
35196         var tab = this.items[id];
35197         tab.enable();
35198     },
35199
35200     /**
35201      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35202      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35203      * @return {Roo.TabPanelItem} The TabPanelItem.
35204      */
35205     activate : function(id){
35206         var tab = this.items[id];
35207         if(!tab){
35208             return null;
35209         }
35210         if(tab == this.active || tab.disabled){
35211             return tab;
35212         }
35213         var e = {};
35214         this.fireEvent("beforetabchange", this, e, tab);
35215         if(e.cancel !== true && !tab.disabled){
35216             if(this.active){
35217                 this.active.hide();
35218             }
35219             this.active = this.items[id];
35220             this.active.show();
35221             this.fireEvent("tabchange", this, this.active);
35222         }
35223         return tab;
35224     },
35225
35226     /**
35227      * Gets the active {@link Roo.TabPanelItem}.
35228      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35229      */
35230     getActiveTab : function(){
35231         return this.active;
35232     },
35233
35234     /**
35235      * Updates the tab body element to fit the height of the container element
35236      * for overflow scrolling
35237      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35238      */
35239     syncHeight : function(targetHeight){
35240         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35241         var bm = this.bodyEl.getMargins();
35242         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35243         this.bodyEl.setHeight(newHeight);
35244         return newHeight;
35245     },
35246
35247     onResize : function(){
35248         if(this.monitorResize){
35249             this.autoSizeTabs();
35250         }
35251     },
35252
35253     /**
35254      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35255      */
35256     beginUpdate : function(){
35257         this.updating = true;
35258     },
35259
35260     /**
35261      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35262      */
35263     endUpdate : function(){
35264         this.updating = false;
35265         this.autoSizeTabs();
35266     },
35267
35268     /**
35269      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35270      */
35271     autoSizeTabs : function(){
35272         var count = this.items.length;
35273         var vcount = count - this.hiddenCount;
35274         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35275             return;
35276         }
35277         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35278         var availWidth = Math.floor(w / vcount);
35279         var b = this.stripBody;
35280         if(b.getWidth() > w){
35281             var tabs = this.items;
35282             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35283             if(availWidth < this.minTabWidth){
35284                 /*if(!this.sleft){    // incomplete scrolling code
35285                     this.createScrollButtons();
35286                 }
35287                 this.showScroll();
35288                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35289             }
35290         }else{
35291             if(this.currentTabWidth < this.preferredTabWidth){
35292                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35293             }
35294         }
35295     },
35296
35297     /**
35298      * Returns the number of tabs in this TabPanel.
35299      * @return {Number}
35300      */
35301      getCount : function(){
35302          return this.items.length;
35303      },
35304
35305     /**
35306      * Resizes all the tabs to the passed width
35307      * @param {Number} The new width
35308      */
35309     setTabWidth : function(width){
35310         this.currentTabWidth = width;
35311         for(var i = 0, len = this.items.length; i < len; i++) {
35312                 if(!this.items[i].isHidden()) {
35313                 this.items[i].setWidth(width);
35314             }
35315         }
35316     },
35317
35318     /**
35319      * Destroys this TabPanel
35320      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35321      */
35322     destroy : function(removeEl){
35323         Roo.EventManager.removeResizeListener(this.onResize, this);
35324         for(var i = 0, len = this.items.length; i < len; i++){
35325             this.items[i].purgeListeners();
35326         }
35327         if(removeEl === true){
35328             this.el.update("");
35329             this.el.remove();
35330         }
35331     },
35332     
35333     createStrip : function(container)
35334     {
35335         var strip = document.createElement("nav");
35336         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35337         container.appendChild(strip);
35338         return strip;
35339     },
35340     
35341     createStripList : function(strip)
35342     {
35343         // div wrapper for retard IE
35344         // returns the "tr" element.
35345         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35346         //'<div class="x-tabs-strip-wrap">'+
35347           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35348           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35349         return strip.firstChild; //.firstChild.firstChild.firstChild;
35350     },
35351     createBody : function(container)
35352     {
35353         var body = document.createElement("div");
35354         Roo.id(body, "tab-body");
35355         //Roo.fly(body).addClass("x-tabs-body");
35356         Roo.fly(body).addClass("tab-content");
35357         container.appendChild(body);
35358         return body;
35359     },
35360     createItemBody :function(bodyEl, id){
35361         var body = Roo.getDom(id);
35362         if(!body){
35363             body = document.createElement("div");
35364             body.id = id;
35365         }
35366         //Roo.fly(body).addClass("x-tabs-item-body");
35367         Roo.fly(body).addClass("tab-pane");
35368          bodyEl.insertBefore(body, bodyEl.firstChild);
35369         return body;
35370     },
35371     /** @private */
35372     createStripElements :  function(stripEl, text, closable)
35373     {
35374         var td = document.createElement("li"); // was td..
35375         
35376         
35377         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35378         
35379         
35380         stripEl.appendChild(td);
35381         /*if(closable){
35382             td.className = "x-tabs-closable";
35383             if(!this.closeTpl){
35384                 this.closeTpl = new Roo.Template(
35385                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35386                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35387                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35388                 );
35389             }
35390             var el = this.closeTpl.overwrite(td, {"text": text});
35391             var close = el.getElementsByTagName("div")[0];
35392             var inner = el.getElementsByTagName("em")[0];
35393             return {"el": el, "close": close, "inner": inner};
35394         } else {
35395         */
35396         // not sure what this is..
35397             if(!this.tabTpl){
35398                 //this.tabTpl = new Roo.Template(
35399                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35400                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35401                 //);
35402                 this.tabTpl = new Roo.Template(
35403                    '<a href="#">' +
35404                    '<span unselectable="on"' +
35405                             (this.disableTooltips ? '' : ' title="{text}"') +
35406                             ' >{text}</span></span></a>'
35407                 );
35408                 
35409             }
35410             var el = this.tabTpl.overwrite(td, {"text": text});
35411             var inner = el.getElementsByTagName("span")[0];
35412             return {"el": el, "inner": inner};
35413         //}
35414     }
35415         
35416     
35417 });
35418
35419 /**
35420  * @class Roo.TabPanelItem
35421  * @extends Roo.util.Observable
35422  * Represents an individual item (tab plus body) in a TabPanel.
35423  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35424  * @param {String} id The id of this TabPanelItem
35425  * @param {String} text The text for the tab of this TabPanelItem
35426  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35427  */
35428 Roo.bootstrap.panel.TabItem = function(config){
35429     /**
35430      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35431      * @type Roo.TabPanel
35432      */
35433     this.tabPanel = config.panel;
35434     /**
35435      * The id for this TabPanelItem
35436      * @type String
35437      */
35438     this.id = config.id;
35439     /** @private */
35440     this.disabled = false;
35441     /** @private */
35442     this.text = config.text;
35443     /** @private */
35444     this.loaded = false;
35445     this.closable = config.closable;
35446
35447     /**
35448      * The body element for this TabPanelItem.
35449      * @type Roo.Element
35450      */
35451     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35452     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35453     this.bodyEl.setStyle("display", "block");
35454     this.bodyEl.setStyle("zoom", "1");
35455     //this.hideAction();
35456
35457     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35458     /** @private */
35459     this.el = Roo.get(els.el);
35460     this.inner = Roo.get(els.inner, true);
35461     this.textEl = Roo.get(this.el.dom.firstChild, true);
35462     this.pnode = Roo.get(els.el.parentNode, true);
35463     this.el.on("mousedown", this.onTabMouseDown, this);
35464     this.el.on("click", this.onTabClick, this);
35465     /** @private */
35466     if(config.closable){
35467         var c = Roo.get(els.close, true);
35468         c.dom.title = this.closeText;
35469         c.addClassOnOver("close-over");
35470         c.on("click", this.closeClick, this);
35471      }
35472
35473     this.addEvents({
35474          /**
35475          * @event activate
35476          * Fires when this tab becomes the active tab.
35477          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35478          * @param {Roo.TabPanelItem} this
35479          */
35480         "activate": true,
35481         /**
35482          * @event beforeclose
35483          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35484          * @param {Roo.TabPanelItem} this
35485          * @param {Object} e Set cancel to true on this object to cancel the close.
35486          */
35487         "beforeclose": true,
35488         /**
35489          * @event close
35490          * Fires when this tab is closed.
35491          * @param {Roo.TabPanelItem} this
35492          */
35493          "close": true,
35494         /**
35495          * @event deactivate
35496          * Fires when this tab is no longer the active tab.
35497          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35498          * @param {Roo.TabPanelItem} this
35499          */
35500          "deactivate" : true
35501     });
35502     this.hidden = false;
35503
35504     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35505 };
35506
35507 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35508            {
35509     purgeListeners : function(){
35510        Roo.util.Observable.prototype.purgeListeners.call(this);
35511        this.el.removeAllListeners();
35512     },
35513     /**
35514      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35515      */
35516     show : function(){
35517         this.pnode.addClass("active");
35518         this.showAction();
35519         if(Roo.isOpera){
35520             this.tabPanel.stripWrap.repaint();
35521         }
35522         this.fireEvent("activate", this.tabPanel, this);
35523     },
35524
35525     /**
35526      * Returns true if this tab is the active tab.
35527      * @return {Boolean}
35528      */
35529     isActive : function(){
35530         return this.tabPanel.getActiveTab() == this;
35531     },
35532
35533     /**
35534      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35535      */
35536     hide : function(){
35537         this.pnode.removeClass("active");
35538         this.hideAction();
35539         this.fireEvent("deactivate", this.tabPanel, this);
35540     },
35541
35542     hideAction : function(){
35543         this.bodyEl.hide();
35544         this.bodyEl.setStyle("position", "absolute");
35545         this.bodyEl.setLeft("-20000px");
35546         this.bodyEl.setTop("-20000px");
35547     },
35548
35549     showAction : function(){
35550         this.bodyEl.setStyle("position", "relative");
35551         this.bodyEl.setTop("");
35552         this.bodyEl.setLeft("");
35553         this.bodyEl.show();
35554     },
35555
35556     /**
35557      * Set the tooltip for the tab.
35558      * @param {String} tooltip The tab's tooltip
35559      */
35560     setTooltip : function(text){
35561         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35562             this.textEl.dom.qtip = text;
35563             this.textEl.dom.removeAttribute('title');
35564         }else{
35565             this.textEl.dom.title = text;
35566         }
35567     },
35568
35569     onTabClick : function(e){
35570         e.preventDefault();
35571         this.tabPanel.activate(this.id);
35572     },
35573
35574     onTabMouseDown : function(e){
35575         e.preventDefault();
35576         this.tabPanel.activate(this.id);
35577     },
35578 /*
35579     getWidth : function(){
35580         return this.inner.getWidth();
35581     },
35582
35583     setWidth : function(width){
35584         var iwidth = width - this.pnode.getPadding("lr");
35585         this.inner.setWidth(iwidth);
35586         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35587         this.pnode.setWidth(width);
35588     },
35589 */
35590     /**
35591      * Show or hide the tab
35592      * @param {Boolean} hidden True to hide or false to show.
35593      */
35594     setHidden : function(hidden){
35595         this.hidden = hidden;
35596         this.pnode.setStyle("display", hidden ? "none" : "");
35597     },
35598
35599     /**
35600      * Returns true if this tab is "hidden"
35601      * @return {Boolean}
35602      */
35603     isHidden : function(){
35604         return this.hidden;
35605     },
35606
35607     /**
35608      * Returns the text for this tab
35609      * @return {String}
35610      */
35611     getText : function(){
35612         return this.text;
35613     },
35614     /*
35615     autoSize : function(){
35616         //this.el.beginMeasure();
35617         this.textEl.setWidth(1);
35618         /*
35619          *  #2804 [new] Tabs in Roojs
35620          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35621          */
35622         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35623         //this.el.endMeasure();
35624     //},
35625
35626     /**
35627      * Sets the text for the tab (Note: this also sets the tooltip text)
35628      * @param {String} text The tab's text and tooltip
35629      */
35630     setText : function(text){
35631         this.text = text;
35632         this.textEl.update(text);
35633         this.setTooltip(text);
35634         //if(!this.tabPanel.resizeTabs){
35635         //    this.autoSize();
35636         //}
35637     },
35638     /**
35639      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35640      */
35641     activate : function(){
35642         this.tabPanel.activate(this.id);
35643     },
35644
35645     /**
35646      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35647      */
35648     disable : function(){
35649         if(this.tabPanel.active != this){
35650             this.disabled = true;
35651             this.pnode.addClass("disabled");
35652         }
35653     },
35654
35655     /**
35656      * Enables this TabPanelItem if it was previously disabled.
35657      */
35658     enable : function(){
35659         this.disabled = false;
35660         this.pnode.removeClass("disabled");
35661     },
35662
35663     /**
35664      * Sets the content for this TabPanelItem.
35665      * @param {String} content The content
35666      * @param {Boolean} loadScripts true to look for and load scripts
35667      */
35668     setContent : function(content, loadScripts){
35669         this.bodyEl.update(content, loadScripts);
35670     },
35671
35672     /**
35673      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35674      * @return {Roo.UpdateManager} The UpdateManager
35675      */
35676     getUpdateManager : function(){
35677         return this.bodyEl.getUpdateManager();
35678     },
35679
35680     /**
35681      * Set a URL to be used to load the content for this TabPanelItem.
35682      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35683      * @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)
35684      * @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)
35685      * @return {Roo.UpdateManager} The UpdateManager
35686      */
35687     setUrl : function(url, params, loadOnce){
35688         if(this.refreshDelegate){
35689             this.un('activate', this.refreshDelegate);
35690         }
35691         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35692         this.on("activate", this.refreshDelegate);
35693         return this.bodyEl.getUpdateManager();
35694     },
35695
35696     /** @private */
35697     _handleRefresh : function(url, params, loadOnce){
35698         if(!loadOnce || !this.loaded){
35699             var updater = this.bodyEl.getUpdateManager();
35700             updater.update(url, params, this._setLoaded.createDelegate(this));
35701         }
35702     },
35703
35704     /**
35705      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35706      *   Will fail silently if the setUrl method has not been called.
35707      *   This does not activate the panel, just updates its content.
35708      */
35709     refresh : function(){
35710         if(this.refreshDelegate){
35711            this.loaded = false;
35712            this.refreshDelegate();
35713         }
35714     },
35715
35716     /** @private */
35717     _setLoaded : function(){
35718         this.loaded = true;
35719     },
35720
35721     /** @private */
35722     closeClick : function(e){
35723         var o = {};
35724         e.stopEvent();
35725         this.fireEvent("beforeclose", this, o);
35726         if(o.cancel !== true){
35727             this.tabPanel.removeTab(this.id);
35728         }
35729     },
35730     /**
35731      * The text displayed in the tooltip for the close icon.
35732      * @type String
35733      */
35734     closeText : "Close this tab"
35735 });