5c4055f05ea84889b5781f29792c6ac59f088aaa
[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
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @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)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     bodyEl : function()
1297     {
1298         if(!this.el || !this.panel.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-body',true).first()
1303     },
1304     
1305     titleEl : function()
1306     {
1307         if(!this.el || !this.panel.length || !this.header.length){
1308             return;
1309         }
1310         
1311         return this.el.select('.panel-title',true).first();
1312     },
1313     
1314     setTitle : function(v)
1315     {
1316         var titleEl = this.titleEl();
1317         
1318         if(!titleEl){
1319             return;
1320         }
1321         
1322         titleEl.dom.innerHTML = v;
1323     },
1324     
1325     getTitle : function()
1326     {
1327         
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return '';
1332         }
1333         
1334         return titleEl.dom.innerHTML;
1335     },
1336     
1337     setRightTitle : function(v)
1338     {
1339         var t = this.el.select('.panel-header-right',true).first();
1340         
1341         if(!t){
1342             return;
1343         }
1344         
1345         t.dom.innerHTML = v;
1346     },
1347     
1348     onClick : function(e)
1349     {
1350         e.preventDefault();
1351         
1352         this.fireEvent('click', this, e);
1353     }
1354    
1355 });
1356
1357  /*
1358  * - LGPL
1359  *
1360  * image
1361  * 
1362  */
1363
1364
1365 /**
1366  * @class Roo.bootstrap.Img
1367  * @extends Roo.bootstrap.Component
1368  * Bootstrap Img class
1369  * @cfg {Boolean} imgResponsive false | true
1370  * @cfg {String} border rounded | circle | thumbnail
1371  * @cfg {String} src image source
1372  * @cfg {String} alt image alternative text
1373  * @cfg {String} href a tag href
1374  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1375  * @cfg {String} xsUrl xs image source
1376  * @cfg {String} smUrl sm image source
1377  * @cfg {String} mdUrl md image source
1378  * @cfg {String} lgUrl lg image source
1379  * 
1380  * @constructor
1381  * Create a new Input
1382  * @param {Object} config The config object
1383  */
1384
1385 Roo.bootstrap.Img = function(config){
1386     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1387     
1388     this.addEvents({
1389         // img events
1390         /**
1391          * @event click
1392          * The img click event for the img.
1393          * @param {Roo.EventObject} e
1394          */
1395         "click" : true
1396     });
1397 };
1398
1399 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1400     
1401     imgResponsive: true,
1402     border: '',
1403     src: 'about:blank',
1404     href: false,
1405     target: false,
1406     xsUrl: '',
1407     smUrl: '',
1408     mdUrl: '',
1409     lgUrl: '',
1410
1411     getAutoCreate : function()
1412     {   
1413         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1414             return this.createSingleImg();
1415         }
1416         
1417         var cfg = {
1418             tag: 'div',
1419             cls: 'roo-image-responsive-group',
1420             cn: []
1421         };
1422         var _this = this;
1423         
1424         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1425             
1426             if(!_this[size + 'Url']){
1427                 return;
1428             }
1429             
1430             var img = {
1431                 tag: 'img',
1432                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1433                 html: _this.html || cfg.html,
1434                 src: _this[size + 'Url']
1435             };
1436             
1437             img.cls += ' roo-image-responsive-' + size;
1438             
1439             var s = ['xs', 'sm', 'md', 'lg'];
1440             
1441             s.splice(s.indexOf(size), 1);
1442             
1443             Roo.each(s, function(ss){
1444                 img.cls += ' hidden-' + ss;
1445             });
1446             
1447             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1448                 cfg.cls += ' img-' + _this.border;
1449             }
1450             
1451             if(_this.alt){
1452                 cfg.alt = _this.alt;
1453             }
1454             
1455             if(_this.href){
1456                 var a = {
1457                     tag: 'a',
1458                     href: _this.href,
1459                     cn: [
1460                         img
1461                     ]
1462                 };
1463
1464                 if(this.target){
1465                     a.target = _this.target;
1466                 }
1467             }
1468             
1469             cfg.cn.push((_this.href) ? a : img);
1470             
1471         });
1472         
1473         return cfg;
1474     },
1475     
1476     createSingleImg : function()
1477     {
1478         var cfg = {
1479             tag: 'img',
1480             cls: (this.imgResponsive) ? 'img-responsive' : '',
1481             html : null,
1482             src : 'about:blank'  // just incase src get's set to undefined?!?
1483         };
1484         
1485         cfg.html = this.html || cfg.html;
1486         
1487         cfg.src = this.src || cfg.src;
1488         
1489         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1490             cfg.cls += ' img-' + this.border;
1491         }
1492         
1493         if(this.alt){
1494             cfg.alt = this.alt;
1495         }
1496         
1497         if(this.href){
1498             var a = {
1499                 tag: 'a',
1500                 href: this.href,
1501                 cn: [
1502                     cfg
1503                 ]
1504             };
1505             
1506             if(this.target){
1507                 a.target = this.target;
1508             }
1509             
1510         }
1511         
1512         return (this.href) ? a : cfg;
1513     },
1514     
1515     initEvents: function() 
1516     {
1517         if(!this.href){
1518             this.el.on('click', this.onClick, this);
1519         }
1520         
1521     },
1522     
1523     onClick : function(e)
1524     {
1525         Roo.log('img onclick');
1526         this.fireEvent('click', this, e);
1527     },
1528     /**
1529      * Sets the url of the image - used to update it
1530      * @param {String} url the url of the image
1531      */
1532     
1533     setSrc : function(url)
1534     {
1535         this.src =  url;
1536         
1537         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1538             this.el.dom.src =  url;
1539             return;
1540         }
1541         
1542         this.el.select('img', true).first().dom.src =  url;
1543     }
1544     
1545     
1546    
1547 });
1548
1549  /*
1550  * - LGPL
1551  *
1552  * image
1553  * 
1554  */
1555
1556
1557 /**
1558  * @class Roo.bootstrap.Link
1559  * @extends Roo.bootstrap.Component
1560  * Bootstrap Link Class
1561  * @cfg {String} alt image alternative text
1562  * @cfg {String} href a tag href
1563  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1564  * @cfg {String} html the content of the link.
1565  * @cfg {String} anchor name for the anchor link
1566  * @cfg {String} fa - favicon
1567
1568  * @cfg {Boolean} preventDefault (true | false) default false
1569
1570  * 
1571  * @constructor
1572  * Create a new Input
1573  * @param {Object} config The config object
1574  */
1575
1576 Roo.bootstrap.Link = function(config){
1577     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1578     
1579     this.addEvents({
1580         // img events
1581         /**
1582          * @event click
1583          * The img click event for the img.
1584          * @param {Roo.EventObject} e
1585          */
1586         "click" : true
1587     });
1588 };
1589
1590 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1591     
1592     href: false,
1593     target: false,
1594     preventDefault: false,
1595     anchor : false,
1596     alt : false,
1597     fa: false,
1598
1599
1600     getAutoCreate : function()
1601     {
1602         var html = this.html || '';
1603         
1604         if (this.fa !== false) {
1605             html = '<i class="fa fa-' + this.fa + '"></i>';
1606         }
1607         var cfg = {
1608             tag: 'a'
1609         };
1610         // anchor's do not require html/href...
1611         if (this.anchor === false) {
1612             cfg.html = html;
1613             cfg.href = this.href || '#';
1614         } else {
1615             cfg.name = this.anchor;
1616             if (this.html !== false || this.fa !== false) {
1617                 cfg.html = html;
1618             }
1619             if (this.href !== false) {
1620                 cfg.href = this.href;
1621             }
1622         }
1623         
1624         if(this.alt !== false){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         
1629         if(this.target !== false) {
1630             cfg.target = this.target;
1631         }
1632         
1633         return cfg;
1634     },
1635     
1636     initEvents: function() {
1637         
1638         if(!this.href || this.preventDefault){
1639             this.el.on('click', this.onClick, this);
1640         }
1641     },
1642     
1643     onClick : function(e)
1644     {
1645         if(this.preventDefault){
1646             e.preventDefault();
1647         }
1648         //Roo.log('img onclick');
1649         this.fireEvent('click', this, e);
1650     }
1651    
1652 });
1653
1654  /*
1655  * - LGPL
1656  *
1657  * header
1658  * 
1659  */
1660
1661 /**
1662  * @class Roo.bootstrap.Header
1663  * @extends Roo.bootstrap.Component
1664  * Bootstrap Header class
1665  * @cfg {String} html content of header
1666  * @cfg {Number} level (1|2|3|4|5|6) default 1
1667  * 
1668  * @constructor
1669  * Create a new Header
1670  * @param {Object} config The config object
1671  */
1672
1673
1674 Roo.bootstrap.Header  = function(config){
1675     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1676 };
1677
1678 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1679     
1680     //href : false,
1681     html : false,
1682     level : 1,
1683     
1684     
1685     
1686     getAutoCreate : function(){
1687         
1688         
1689         
1690         var cfg = {
1691             tag: 'h' + (1 *this.level),
1692             html: this.html || ''
1693         } ;
1694         
1695         return cfg;
1696     }
1697    
1698 });
1699
1700  
1701
1702  /*
1703  * Based on:
1704  * Ext JS Library 1.1.1
1705  * Copyright(c) 2006-2007, Ext JS, LLC.
1706  *
1707  * Originally Released Under LGPL - original licence link has changed is not relivant.
1708  *
1709  * Fork - LGPL
1710  * <script type="text/javascript">
1711  */
1712  
1713 /**
1714  * @class Roo.bootstrap.MenuMgr
1715  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1716  * @singleton
1717  */
1718 Roo.bootstrap.MenuMgr = function(){
1719    var menus, active, groups = {}, attached = false, lastShow = new Date();
1720
1721    // private - called when first menu is created
1722    function init(){
1723        menus = {};
1724        active = new Roo.util.MixedCollection();
1725        Roo.get(document).addKeyListener(27, function(){
1726            if(active.length > 0){
1727                hideAll();
1728            }
1729        });
1730    }
1731
1732    // private
1733    function hideAll(){
1734        if(active && active.length > 0){
1735            var c = active.clone();
1736            c.each(function(m){
1737                m.hide();
1738            });
1739        }
1740    }
1741
1742    // private
1743    function onHide(m){
1744        active.remove(m);
1745        if(active.length < 1){
1746            Roo.get(document).un("mouseup", onMouseDown);
1747             
1748            attached = false;
1749        }
1750    }
1751
1752    // private
1753    function onShow(m){
1754        var last = active.last();
1755        lastShow = new Date();
1756        active.add(m);
1757        if(!attached){
1758           Roo.get(document).on("mouseup", onMouseDown);
1759            
1760            attached = true;
1761        }
1762        if(m.parentMenu){
1763           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1764           m.parentMenu.activeChild = m;
1765        }else if(last && last.isVisible()){
1766           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1767        }
1768    }
1769
1770    // private
1771    function onBeforeHide(m){
1772        if(m.activeChild){
1773            m.activeChild.hide();
1774        }
1775        if(m.autoHideTimer){
1776            clearTimeout(m.autoHideTimer);
1777            delete m.autoHideTimer;
1778        }
1779    }
1780
1781    // private
1782    function onBeforeShow(m){
1783        var pm = m.parentMenu;
1784        if(!pm && !m.allowOtherMenus){
1785            hideAll();
1786        }else if(pm && pm.activeChild && active != m){
1787            pm.activeChild.hide();
1788        }
1789    }
1790
1791    // private this should really trigger on mouseup..
1792    function onMouseDown(e){
1793         Roo.log("on Mouse Up");
1794         
1795         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1796             Roo.log("MenuManager hideAll");
1797             hideAll();
1798             e.stopEvent();
1799         }
1800         
1801         
1802    }
1803
1804    // private
1805    function onBeforeCheck(mi, state){
1806        if(state){
1807            var g = groups[mi.group];
1808            for(var i = 0, l = g.length; i < l; i++){
1809                if(g[i] != mi){
1810                    g[i].setChecked(false);
1811                }
1812            }
1813        }
1814    }
1815
1816    return {
1817
1818        /**
1819         * Hides all menus that are currently visible
1820         */
1821        hideAll : function(){
1822             hideAll();  
1823        },
1824
1825        // private
1826        register : function(menu){
1827            if(!menus){
1828                init();
1829            }
1830            menus[menu.id] = menu;
1831            menu.on("beforehide", onBeforeHide);
1832            menu.on("hide", onHide);
1833            menu.on("beforeshow", onBeforeShow);
1834            menu.on("show", onShow);
1835            var g = menu.group;
1836            if(g && menu.events["checkchange"]){
1837                if(!groups[g]){
1838                    groups[g] = [];
1839                }
1840                groups[g].push(menu);
1841                menu.on("checkchange", onCheck);
1842            }
1843        },
1844
1845         /**
1846          * Returns a {@link Roo.menu.Menu} object
1847          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1848          * be used to generate and return a new Menu instance.
1849          */
1850        get : function(menu){
1851            if(typeof menu == "string"){ // menu id
1852                return menus[menu];
1853            }else if(menu.events){  // menu instance
1854                return menu;
1855            }
1856            /*else if(typeof menu.length == 'number'){ // array of menu items?
1857                return new Roo.bootstrap.Menu({items:menu});
1858            }else{ // otherwise, must be a config
1859                return new Roo.bootstrap.Menu(menu);
1860            }
1861            */
1862            return false;
1863        },
1864
1865        // private
1866        unregister : function(menu){
1867            delete menus[menu.id];
1868            menu.un("beforehide", onBeforeHide);
1869            menu.un("hide", onHide);
1870            menu.un("beforeshow", onBeforeShow);
1871            menu.un("show", onShow);
1872            var g = menu.group;
1873            if(g && menu.events["checkchange"]){
1874                groups[g].remove(menu);
1875                menu.un("checkchange", onCheck);
1876            }
1877        },
1878
1879        // private
1880        registerCheckable : function(menuItem){
1881            var g = menuItem.group;
1882            if(g){
1883                if(!groups[g]){
1884                    groups[g] = [];
1885                }
1886                groups[g].push(menuItem);
1887                menuItem.on("beforecheckchange", onBeforeCheck);
1888            }
1889        },
1890
1891        // private
1892        unregisterCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                groups[g].remove(menuItem);
1896                menuItem.un("beforecheckchange", onBeforeCheck);
1897            }
1898        }
1899    };
1900 }();/*
1901  * - LGPL
1902  *
1903  * menu
1904  * 
1905  */
1906
1907 /**
1908  * @class Roo.bootstrap.Menu
1909  * @extends Roo.bootstrap.Component
1910  * Bootstrap Menu class - container for MenuItems
1911  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1912  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1913  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1914  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1915  * 
1916  * @constructor
1917  * Create a new Menu
1918  * @param {Object} config The config object
1919  */
1920
1921
1922 Roo.bootstrap.Menu = function(config){
1923     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1924     if (this.registerMenu && this.type != 'treeview')  {
1925         Roo.bootstrap.MenuMgr.register(this);
1926     }
1927     this.addEvents({
1928         /**
1929          * @event beforeshow
1930          * Fires before this menu is displayed
1931          * @param {Roo.menu.Menu} this
1932          */
1933         beforeshow : true,
1934         /**
1935          * @event beforehide
1936          * Fires before this menu is hidden
1937          * @param {Roo.menu.Menu} this
1938          */
1939         beforehide : true,
1940         /**
1941          * @event show
1942          * Fires after this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         show : true,
1946         /**
1947          * @event hide
1948          * Fires after this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         hide : true,
1952         /**
1953          * @event click
1954          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1955          * @param {Roo.menu.Menu} this
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          * @param {Roo.EventObject} e
1958          */
1959         click : true,
1960         /**
1961          * @event mouseover
1962          * Fires when the mouse is hovering over this menu
1963          * @param {Roo.menu.Menu} this
1964          * @param {Roo.EventObject} e
1965          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1966          */
1967         mouseover : true,
1968         /**
1969          * @event mouseout
1970          * Fires when the mouse exits this menu
1971          * @param {Roo.menu.Menu} this
1972          * @param {Roo.EventObject} e
1973          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1974          */
1975         mouseout : true,
1976         /**
1977          * @event itemclick
1978          * Fires when a menu item contained in this menu is clicked
1979          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1980          * @param {Roo.EventObject} e
1981          */
1982         itemclick: true
1983     });
1984     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1985 };
1986
1987 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1988     
1989    /// html : false,
1990     //align : '',
1991     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1992     type: false,
1993     /**
1994      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1995      */
1996     registerMenu : true,
1997     
1998     menuItems :false, // stores the menu items..
1999     
2000     hidden:true,
2001         
2002     parentMenu : false,
2003     
2004     stopEvent : true,
2005     
2006     isLink : false,
2007     
2008     getChildContainer : function() {
2009         return this.el;  
2010     },
2011     
2012     getAutoCreate : function(){
2013          
2014         //if (['right'].indexOf(this.align)!==-1) {
2015         //    cfg.cn[1].cls += ' pull-right'
2016         //}
2017         
2018         
2019         var cfg = {
2020             tag : 'ul',
2021             cls : 'dropdown-menu' ,
2022             style : 'z-index:1000'
2023             
2024         };
2025         
2026         if (this.type === 'submenu') {
2027             cfg.cls = 'submenu active';
2028         }
2029         if (this.type === 'treeview') {
2030             cfg.cls = 'treeview-menu';
2031         }
2032         
2033         return cfg;
2034     },
2035     initEvents : function() {
2036         
2037        // Roo.log("ADD event");
2038        // Roo.log(this.triggerEl.dom);
2039         
2040         this.triggerEl.on('click', this.onTriggerClick, this);
2041         
2042         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2043         
2044         this.triggerEl.addClass('dropdown-toggle');
2045         
2046         if (Roo.isTouch) {
2047             this.el.on('touchstart'  , this.onTouch, this);
2048         }
2049         this.el.on('click' , this.onClick, this);
2050
2051         this.el.on("mouseover", this.onMouseOver, this);
2052         this.el.on("mouseout", this.onMouseOut, this);
2053         
2054     },
2055     
2056     findTargetItem : function(e)
2057     {
2058         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2059         if(!t){
2060             return false;
2061         }
2062         //Roo.log(t);         Roo.log(t.id);
2063         if(t && t.id){
2064             //Roo.log(this.menuitems);
2065             return this.menuitems.get(t.id);
2066             
2067             //return this.items.get(t.menuItemId);
2068         }
2069         
2070         return false;
2071     },
2072     
2073     onTouch : function(e) 
2074     {
2075         Roo.log("menu.onTouch");
2076         //e.stopEvent(); this make the user popdown broken
2077         this.onClick(e);
2078     },
2079     
2080     onClick : function(e)
2081     {
2082         Roo.log("menu.onClick");
2083         
2084         var t = this.findTargetItem(e);
2085         if(!t || t.isContainer){
2086             return;
2087         }
2088         Roo.log(e);
2089         /*
2090         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2091             if(t == this.activeItem && t.shouldDeactivate(e)){
2092                 this.activeItem.deactivate();
2093                 delete this.activeItem;
2094                 return;
2095             }
2096             if(t.canActivate){
2097                 this.setActiveItem(t, true);
2098             }
2099             return;
2100             
2101             
2102         }
2103         */
2104        
2105         Roo.log('pass click event');
2106         
2107         t.onClick(e);
2108         
2109         this.fireEvent("click", this, t, e);
2110         
2111         var _this = this;
2112         
2113         (function() { _this.hide(); }).defer(100);
2114     },
2115     
2116     onMouseOver : function(e){
2117         var t  = this.findTargetItem(e);
2118         //Roo.log(t);
2119         //if(t){
2120         //    if(t.canActivate && !t.disabled){
2121         //        this.setActiveItem(t, true);
2122         //    }
2123         //}
2124         
2125         this.fireEvent("mouseover", this, e, t);
2126     },
2127     isVisible : function(){
2128         return !this.hidden;
2129     },
2130      onMouseOut : function(e){
2131         var t  = this.findTargetItem(e);
2132         
2133         //if(t ){
2134         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2135         //        this.activeItem.deactivate();
2136         //        delete this.activeItem;
2137         //    }
2138         //}
2139         this.fireEvent("mouseout", this, e, t);
2140     },
2141     
2142     
2143     /**
2144      * Displays this menu relative to another element
2145      * @param {String/HTMLElement/Roo.Element} element The element to align to
2146      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2147      * the element (defaults to this.defaultAlign)
2148      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2149      */
2150     show : function(el, pos, parentMenu){
2151         this.parentMenu = parentMenu;
2152         if(!this.el){
2153             this.render();
2154         }
2155         this.fireEvent("beforeshow", this);
2156         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2157     },
2158      /**
2159      * Displays this menu at a specific xy position
2160      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2161      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2162      */
2163     showAt : function(xy, parentMenu, /* private: */_e){
2164         this.parentMenu = parentMenu;
2165         if(!this.el){
2166             this.render();
2167         }
2168         if(_e !== false){
2169             this.fireEvent("beforeshow", this);
2170             //xy = this.el.adjustForConstraints(xy);
2171         }
2172         
2173         //this.el.show();
2174         this.hideMenuItems();
2175         this.hidden = false;
2176         this.triggerEl.addClass('open');
2177         
2178         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2179             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2180         }
2181         
2182         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2183             this.el.setXY(xy);
2184         }
2185         
2186         this.focus();
2187         this.fireEvent("show", this);
2188     },
2189     
2190     focus : function(){
2191         return;
2192         if(!this.hidden){
2193             this.doFocus.defer(50, this);
2194         }
2195     },
2196
2197     doFocus : function(){
2198         if(!this.hidden){
2199             this.focusEl.focus();
2200         }
2201     },
2202
2203     /**
2204      * Hides this menu and optionally all parent menus
2205      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2206      */
2207     hide : function(deep)
2208     {
2209         
2210         this.hideMenuItems();
2211         if(this.el && this.isVisible()){
2212             this.fireEvent("beforehide", this);
2213             if(this.activeItem){
2214                 this.activeItem.deactivate();
2215                 this.activeItem = null;
2216             }
2217             this.triggerEl.removeClass('open');;
2218             this.hidden = true;
2219             this.fireEvent("hide", this);
2220         }
2221         if(deep === true && this.parentMenu){
2222             this.parentMenu.hide(true);
2223         }
2224     },
2225     
2226     onTriggerClick : function(e)
2227     {
2228         Roo.log('trigger click');
2229         
2230         var target = e.getTarget();
2231         
2232         Roo.log(target.nodeName.toLowerCase());
2233         
2234         if(target.nodeName.toLowerCase() === 'i'){
2235             e.preventDefault();
2236         }
2237         
2238     },
2239     
2240     onTriggerPress  : function(e)
2241     {
2242         Roo.log('trigger press');
2243         //Roo.log(e.getTarget());
2244        // Roo.log(this.triggerEl.dom);
2245        
2246         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2247         var pel = Roo.get(e.getTarget());
2248         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2249             Roo.log('is treeview or dropdown?');
2250             return;
2251         }
2252         
2253         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2254             return;
2255         }
2256         
2257         if (this.isVisible()) {
2258             Roo.log('hide');
2259             this.hide();
2260         } else {
2261             Roo.log('show');
2262             this.show(this.triggerEl, false, false);
2263         }
2264         
2265         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2266             e.stopEvent();
2267         }
2268         
2269     },
2270        
2271     
2272     hideMenuItems : function()
2273     {
2274         Roo.log("hide Menu Items");
2275         if (!this.el) { 
2276             return;
2277         }
2278         //$(backdrop).remove()
2279         this.el.select('.open',true).each(function(aa) {
2280             
2281             aa.removeClass('open');
2282           //var parent = getParent($(this))
2283           //var relatedTarget = { relatedTarget: this }
2284           
2285            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2286           //if (e.isDefaultPrevented()) return
2287            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2288         });
2289     },
2290     addxtypeChild : function (tree, cntr) {
2291         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2292           
2293         this.menuitems.add(comp);
2294         return comp;
2295
2296     },
2297     getEl : function()
2298     {
2299         Roo.log(this.el);
2300         return this.el;
2301     }
2302 });
2303
2304  
2305  /*
2306  * - LGPL
2307  *
2308  * menu item
2309  * 
2310  */
2311
2312
2313 /**
2314  * @class Roo.bootstrap.MenuItem
2315  * @extends Roo.bootstrap.Component
2316  * Bootstrap MenuItem class
2317  * @cfg {String} html the menu label
2318  * @cfg {String} href the link
2319  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2320  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2321  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2322  * @cfg {String} fa favicon to show on left of menu item.
2323  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2324  * 
2325  * 
2326  * @constructor
2327  * Create a new MenuItem
2328  * @param {Object} config The config object
2329  */
2330
2331
2332 Roo.bootstrap.MenuItem = function(config){
2333     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2334     this.addEvents({
2335         // raw events
2336         /**
2337          * @event click
2338          * The raw click event for the entire grid.
2339          * @param {Roo.bootstrap.MenuItem} this
2340          * @param {Roo.EventObject} e
2341          */
2342         "click" : true
2343     });
2344 };
2345
2346 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2347     
2348     href : false,
2349     html : false,
2350     preventDefault: false,
2351     isContainer : false,
2352     active : false,
2353     fa: false,
2354     
2355     getAutoCreate : function(){
2356         
2357         if(this.isContainer){
2358             return {
2359                 tag: 'li',
2360                 cls: 'dropdown-menu-item'
2361             };
2362         }
2363         var ctag = {
2364             tag: 'span',
2365             html: 'Link'
2366         };
2367         
2368         var anc = {
2369             tag : 'a',
2370             href : '#',
2371             cn : [  ]
2372         };
2373         
2374         if (this.fa !== false) {
2375             anc.cn.push({
2376                 tag : 'i',
2377                 cls : 'fa fa-' + this.fa
2378             });
2379         }
2380         
2381         anc.cn.push(ctag);
2382         
2383         
2384         var cfg= {
2385             tag: 'li',
2386             cls: 'dropdown-menu-item',
2387             cn: [ anc ]
2388         };
2389         if (this.parent().type == 'treeview') {
2390             cfg.cls = 'treeview-menu';
2391         }
2392         if (this.active) {
2393             cfg.cls += ' active';
2394         }
2395         
2396         
2397         
2398         anc.href = this.href || cfg.cn[0].href ;
2399         ctag.html = this.html || cfg.cn[0].html ;
2400         return cfg;
2401     },
2402     
2403     initEvents: function()
2404     {
2405         if (this.parent().type == 'treeview') {
2406             this.el.select('a').on('click', this.onClick, this);
2407         }
2408         if (this.menu) {
2409             this.menu.parentType = this.xtype;
2410             this.menu.triggerEl = this.el;
2411             this.menu = this.addxtype(Roo.apply({}, this.menu));
2412         }
2413         
2414     },
2415     onClick : function(e)
2416     {
2417         Roo.log('item on click ');
2418         
2419         if(this.preventDefault){
2420             e.preventDefault();
2421         }
2422         //this.parent().hideMenuItems();
2423         
2424         this.fireEvent('click', this, e);
2425     },
2426     getEl : function()
2427     {
2428         return this.el;
2429     } 
2430 });
2431
2432  
2433
2434  /*
2435  * - LGPL
2436  *
2437  * menu separator
2438  * 
2439  */
2440
2441
2442 /**
2443  * @class Roo.bootstrap.MenuSeparator
2444  * @extends Roo.bootstrap.Component
2445  * Bootstrap MenuSeparator class
2446  * 
2447  * @constructor
2448  * Create a new MenuItem
2449  * @param {Object} config The config object
2450  */
2451
2452
2453 Roo.bootstrap.MenuSeparator = function(config){
2454     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2455 };
2456
2457 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2458     
2459     getAutoCreate : function(){
2460         var cfg = {
2461             cls: 'divider',
2462             tag : 'li'
2463         };
2464         
2465         return cfg;
2466     }
2467    
2468 });
2469
2470  
2471
2472  
2473 /*
2474 * Licence: LGPL
2475 */
2476
2477 /**
2478  * @class Roo.bootstrap.Modal
2479  * @extends Roo.bootstrap.Component
2480  * Bootstrap Modal class
2481  * @cfg {String} title Title of dialog
2482  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2483  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2484  * @cfg {Boolean} specificTitle default false
2485  * @cfg {Array} buttons Array of buttons or standard button set..
2486  * @cfg {String} buttonPosition (left|right|center) default right
2487  * @cfg {Boolean} animate default true
2488  * @cfg {Boolean} allow_close default true
2489  * @cfg {Boolean} fitwindow default false
2490  * @cfg {String} size (sm|lg) default empty
2491  *
2492  *
2493  * @constructor
2494  * Create a new Modal Dialog
2495  * @param {Object} config The config object
2496  */
2497
2498 Roo.bootstrap.Modal = function(config){
2499     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2500     this.addEvents({
2501         // raw events
2502         /**
2503          * @event btnclick
2504          * The raw btnclick event for the button
2505          * @param {Roo.EventObject} e
2506          */
2507         "btnclick" : true,
2508         /**
2509          * @event resize
2510          * Fire when dialog resize
2511          * @param {Roo.bootstrap.Modal} this
2512          * @param {Roo.EventObject} e
2513          */
2514         "resize" : true
2515     });
2516     this.buttons = this.buttons || [];
2517
2518     if (this.tmpl) {
2519         this.tmpl = Roo.factory(this.tmpl);
2520     }
2521
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2525
2526     title : 'test dialog',
2527
2528     buttons : false,
2529
2530     // set on load...
2531
2532     html: false,
2533
2534     tmp: false,
2535
2536     specificTitle: false,
2537
2538     buttonPosition: 'right',
2539
2540     allow_close : true,
2541
2542     animate : true,
2543
2544     fitwindow: false,
2545
2546
2547      // private
2548     dialogEl: false,
2549     bodyEl:  false,
2550     footerEl:  false,
2551     titleEl:  false,
2552     closeEl:  false,
2553
2554     size: '',
2555
2556
2557     onRender : function(ct, position)
2558     {
2559         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2560
2561         if(!this.el){
2562             var cfg = Roo.apply({},  this.getAutoCreate());
2563             cfg.id = Roo.id();
2564             //if(!cfg.name){
2565             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2566             //}
2567             //if (!cfg.name.length) {
2568             //    delete cfg.name;
2569            // }
2570             if (this.cls) {
2571                 cfg.cls += ' ' + this.cls;
2572             }
2573             if (this.style) {
2574                 cfg.style = this.style;
2575             }
2576             this.el = Roo.get(document.body).createChild(cfg, position);
2577         }
2578         //var type = this.el.dom.type;
2579
2580
2581         if(this.tabIndex !== undefined){
2582             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2583         }
2584
2585         this.dialogEl = this.el.select('.modal-dialog',true).first();
2586         this.bodyEl = this.el.select('.modal-body',true).first();
2587         this.closeEl = this.el.select('.modal-header .close', true).first();
2588         this.headerEl = this.el.select('.modal-header',true).first();
2589         this.titleEl = this.el.select('.modal-title',true).first();
2590         this.footerEl = this.el.select('.modal-footer',true).first();
2591
2592         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2593         this.maskEl.enableDisplayMode("block");
2594         this.maskEl.hide();
2595         //this.el.addClass("x-dlg-modal");
2596
2597         if (this.buttons.length) {
2598             Roo.each(this.buttons, function(bb) {
2599                 var b = Roo.apply({}, bb);
2600                 b.xns = b.xns || Roo.bootstrap;
2601                 b.xtype = b.xtype || 'Button';
2602                 if (typeof(b.listeners) == 'undefined') {
2603                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2604                 }
2605
2606                 var btn = Roo.factory(b);
2607
2608                 btn.render(this.el.select('.modal-footer div').first());
2609
2610             },this);
2611         }
2612         // render the children.
2613         var nitems = [];
2614
2615         if(typeof(this.items) != 'undefined'){
2616             var items = this.items;
2617             delete this.items;
2618
2619             for(var i =0;i < items.length;i++) {
2620                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2621             }
2622         }
2623
2624         this.items = nitems;
2625
2626         // where are these used - they used to be body/close/footer
2627
2628
2629         this.initEvents();
2630         //this.el.addClass([this.fieldClass, this.cls]);
2631
2632     },
2633
2634     getAutoCreate : function(){
2635
2636
2637         var bdy = {
2638                 cls : 'modal-body',
2639                 html : this.html || ''
2640         };
2641
2642         var title = {
2643             tag: 'h4',
2644             cls : 'modal-title',
2645             html : this.title
2646         };
2647
2648         if(this.specificTitle){
2649             title = this.title;
2650
2651         };
2652
2653         var header = [];
2654         if (this.allow_close) {
2655             header.push({
2656                 tag: 'button',
2657                 cls : 'close',
2658                 html : '&times'
2659             });
2660         }
2661
2662         header.push(title);
2663
2664         var size = '';
2665
2666         if(this.size.length){
2667             size = 'modal-' + this.size;
2668         }
2669
2670         var modal = {
2671             cls: "modal",
2672             style : 'display: none',
2673             cn : [
2674                 {
2675                     cls: "modal-dialog " + size,
2676                     cn : [
2677                         {
2678                             cls : "modal-content",
2679                             cn : [
2680                                 {
2681                                     cls : 'modal-header',
2682                                     cn : header
2683                                 },
2684                                 bdy,
2685                                 {
2686                                     cls : 'modal-footer',
2687                                     cn : [
2688                                         {
2689                                             tag: 'div',
2690                                             cls: 'btn-' + this.buttonPosition
2691                                         }
2692                                     ]
2693
2694                                 }
2695
2696
2697                             ]
2698
2699                         }
2700                     ]
2701
2702                 }
2703             ]
2704         };
2705
2706         if(this.animate){
2707             modal.cls += ' fade';
2708         }
2709
2710         return modal;
2711
2712     },
2713     getChildContainer : function() {
2714
2715          return this.bodyEl;
2716
2717     },
2718     getButtonContainer : function() {
2719          return this.el.select('.modal-footer div',true).first();
2720
2721     },
2722     initEvents : function()
2723     {
2724         if (this.allow_close) {
2725             this.closeEl.on('click', this.hide, this);
2726         }
2727         Roo.EventManager.onWindowResize(this.resize, this, true);
2728
2729
2730     },
2731
2732     resize : function()
2733     {
2734         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2735         if (this.fitwindow) {
2736             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2737             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2738             this.setSize(w,h);
2739         }
2740     },
2741
2742     setSize : function(w,h)
2743     {
2744         if (!w && !h) {
2745             return;
2746         }
2747         this.resizeTo(w,h);
2748     },
2749
2750     show : function() {
2751
2752         if (!this.rendered) {
2753             this.render();
2754         }
2755
2756         this.el.setStyle('display', 'block');
2757
2758         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2759             var _this = this;
2760             (function(){
2761                 this.el.addClass('in');
2762             }).defer(50, this);
2763         }else{
2764             this.el.addClass('in');
2765
2766         }
2767
2768         // not sure how we can show data in here..
2769         //if (this.tmpl) {
2770         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2771         //}
2772
2773         Roo.get(document.body).addClass("x-body-masked");
2774         
2775         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2776         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2777         this.maskEl.show();
2778         
2779         this.resize();
2780         
2781         this.fireEvent('show', this);
2782
2783         // set zindex here - otherwise it appears to be ignored...
2784         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2785
2786         (function () {
2787             this.items.forEach( function(e) {
2788                 e.layout ? e.layout() : false;
2789
2790             });
2791         }).defer(100,this);
2792
2793     },
2794     hide : function()
2795     {
2796         if(this.fireEvent("beforehide", this) !== false){
2797             this.maskEl.hide();
2798             Roo.get(document.body).removeClass("x-body-masked");
2799             this.el.removeClass('in');
2800             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2801
2802             if(this.animate){ // why
2803                 var _this = this;
2804                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2805             }else{
2806                 this.el.setStyle('display', 'none');
2807             }
2808             this.fireEvent('hide', this);
2809         }
2810     },
2811
2812     addButton : function(str, cb)
2813     {
2814
2815
2816         var b = Roo.apply({}, { html : str } );
2817         b.xns = b.xns || Roo.bootstrap;
2818         b.xtype = b.xtype || 'Button';
2819         if (typeof(b.listeners) == 'undefined') {
2820             b.listeners = { click : cb.createDelegate(this)  };
2821         }
2822
2823         var btn = Roo.factory(b);
2824
2825         btn.render(this.el.select('.modal-footer div').first());
2826
2827         return btn;
2828
2829     },
2830
2831     setDefaultButton : function(btn)
2832     {
2833         //this.el.select('.modal-footer').()
2834     },
2835     diff : false,
2836
2837     resizeTo: function(w,h)
2838     {
2839         // skip.. ?? why??
2840
2841         this.dialogEl.setWidth(w);
2842         if (this.diff === false) {
2843             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2844         }
2845
2846         this.bodyEl.setHeight(h-this.diff);
2847
2848         this.fireEvent('resize', this);
2849
2850     },
2851     setContentSize  : function(w, h)
2852     {
2853
2854     },
2855     onButtonClick: function(btn,e)
2856     {
2857         //Roo.log([a,b,c]);
2858         this.fireEvent('btnclick', btn.name, e);
2859     },
2860      /**
2861      * Set the title of the Dialog
2862      * @param {String} str new Title
2863      */
2864     setTitle: function(str) {
2865         this.titleEl.dom.innerHTML = str;
2866     },
2867     /**
2868      * Set the body of the Dialog
2869      * @param {String} str new Title
2870      */
2871     setBody: function(str) {
2872         this.bodyEl.dom.innerHTML = str;
2873     },
2874     /**
2875      * Set the body of the Dialog using the template
2876      * @param {Obj} data - apply this data to the template and replace the body contents.
2877      */
2878     applyBody: function(obj)
2879     {
2880         if (!this.tmpl) {
2881             Roo.log("Error - using apply Body without a template");
2882             //code
2883         }
2884         this.tmpl.overwrite(this.bodyEl, obj);
2885     }
2886
2887 });
2888
2889
2890 Roo.apply(Roo.bootstrap.Modal,  {
2891     /**
2892          * Button config that displays a single OK button
2893          * @type Object
2894          */
2895         OK :  [{
2896             name : 'ok',
2897             weight : 'primary',
2898             html : 'OK'
2899         }],
2900         /**
2901          * Button config that displays Yes and No buttons
2902          * @type Object
2903          */
2904         YESNO : [
2905             {
2906                 name  : 'no',
2907                 html : 'No'
2908             },
2909             {
2910                 name  :'yes',
2911                 weight : 'primary',
2912                 html : 'Yes'
2913             }
2914         ],
2915
2916         /**
2917          * Button config that displays OK and Cancel buttons
2918          * @type Object
2919          */
2920         OKCANCEL : [
2921             {
2922                name : 'cancel',
2923                 html : 'Cancel'
2924             },
2925             {
2926                 name : 'ok',
2927                 weight : 'primary',
2928                 html : 'OK'
2929             }
2930         ],
2931         /**
2932          * Button config that displays Yes, No and Cancel buttons
2933          * @type Object
2934          */
2935         YESNOCANCEL : [
2936             {
2937                 name : 'yes',
2938                 weight : 'primary',
2939                 html : 'Yes'
2940             },
2941             {
2942                 name : 'no',
2943                 html : 'No'
2944             },
2945             {
2946                 name : 'cancel',
2947                 html : 'Cancel'
2948             }
2949         ],
2950         
2951         zIndex : 10001
2952 });
2953 /*
2954  * - LGPL
2955  *
2956  * messagebox - can be used as a replace
2957  * 
2958  */
2959 /**
2960  * @class Roo.MessageBox
2961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2962  * Example usage:
2963  *<pre><code>
2964 // Basic alert:
2965 Roo.Msg.alert('Status', 'Changes saved successfully.');
2966
2967 // Prompt for user data:
2968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2969     if (btn == 'ok'){
2970         // process text value...
2971     }
2972 });
2973
2974 // Show a dialog using config options:
2975 Roo.Msg.show({
2976    title:'Save Changes?',
2977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2978    buttons: Roo.Msg.YESNOCANCEL,
2979    fn: processResult,
2980    animEl: 'elId'
2981 });
2982 </code></pre>
2983  * @singleton
2984  */
2985 Roo.bootstrap.MessageBox = function(){
2986     var dlg, opt, mask, waitTimer;
2987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2988     var buttons, activeTextEl, bwidth;
2989
2990     
2991     // private
2992     var handleButton = function(button){
2993         dlg.hide();
2994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2995     };
2996
2997     // private
2998     var handleHide = function(){
2999         if(opt && opt.cls){
3000             dlg.el.removeClass(opt.cls);
3001         }
3002         //if(waitTimer){
3003         //    Roo.TaskMgr.stop(waitTimer);
3004         //    waitTimer = null;
3005         //}
3006     };
3007
3008     // private
3009     var updateButtons = function(b){
3010         var width = 0;
3011         if(!b){
3012             buttons["ok"].hide();
3013             buttons["cancel"].hide();
3014             buttons["yes"].hide();
3015             buttons["no"].hide();
3016             //dlg.footer.dom.style.display = 'none';
3017             return width;
3018         }
3019         dlg.footerEl.dom.style.display = '';
3020         for(var k in buttons){
3021             if(typeof buttons[k] != "function"){
3022                 if(b[k]){
3023                     buttons[k].show();
3024                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3025                     width += buttons[k].el.getWidth()+15;
3026                 }else{
3027                     buttons[k].hide();
3028                 }
3029             }
3030         }
3031         return width;
3032     };
3033
3034     // private
3035     var handleEsc = function(d, k, e){
3036         if(opt && opt.closable !== false){
3037             dlg.hide();
3038         }
3039         if(e){
3040             e.stopEvent();
3041         }
3042     };
3043
3044     return {
3045         /**
3046          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3047          * @return {Roo.BasicDialog} The BasicDialog element
3048          */
3049         getDialog : function(){
3050            if(!dlg){
3051                 dlg = new Roo.bootstrap.Modal( {
3052                     //draggable: true,
3053                     //resizable:false,
3054                     //constraintoviewport:false,
3055                     //fixedcenter:true,
3056                     //collapsible : false,
3057                     //shim:true,
3058                     //modal: true,
3059                 //    width: 'auto',
3060                   //  height:100,
3061                     //buttonAlign:"center",
3062                     closeClick : function(){
3063                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3064                             handleButton("no");
3065                         }else{
3066                             handleButton("cancel");
3067                         }
3068                     }
3069                 });
3070                 dlg.render();
3071                 dlg.on("hide", handleHide);
3072                 mask = dlg.mask;
3073                 //dlg.addKeyListener(27, handleEsc);
3074                 buttons = {};
3075                 this.buttons = buttons;
3076                 var bt = this.buttonText;
3077                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3078                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3079                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3080                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3081                 //Roo.log(buttons);
3082                 bodyEl = dlg.bodyEl.createChild({
3083
3084                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3085                         '<textarea class="roo-mb-textarea"></textarea>' +
3086                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3087                 });
3088                 msgEl = bodyEl.dom.firstChild;
3089                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3090                 textboxEl.enableDisplayMode();
3091                 textboxEl.addKeyListener([10,13], function(){
3092                     if(dlg.isVisible() && opt && opt.buttons){
3093                         if(opt.buttons.ok){
3094                             handleButton("ok");
3095                         }else if(opt.buttons.yes){
3096                             handleButton("yes");
3097                         }
3098                     }
3099                 });
3100                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3101                 textareaEl.enableDisplayMode();
3102                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3103                 progressEl.enableDisplayMode();
3104                 
3105                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3106                 //var pf = progressEl.dom.firstChild;
3107                 //if (pf) {
3108                     //pp = Roo.get(pf.firstChild);
3109                     //pp.setHeight(pf.offsetHeight);
3110                 //}
3111                 
3112             }
3113             return dlg;
3114         },
3115
3116         /**
3117          * Updates the message box body text
3118          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3119          * the XHTML-compliant non-breaking space character '&amp;#160;')
3120          * @return {Roo.MessageBox} This message box
3121          */
3122         updateText : function(text)
3123         {
3124             if(!dlg.isVisible() && !opt.width){
3125                 dlg.dialogEl.setWidth(this.maxWidth);
3126                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3127             }
3128             msgEl.innerHTML = text || '&#160;';
3129       
3130             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3131             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3132             var w = Math.max(
3133                     Math.min(opt.width || cw , this.maxWidth), 
3134                     Math.max(opt.minWidth || this.minWidth, bwidth)
3135             );
3136             if(opt.prompt){
3137                 activeTextEl.setWidth(w);
3138             }
3139             if(dlg.isVisible()){
3140                 dlg.fixedcenter = false;
3141             }
3142             // to big, make it scroll. = But as usual stupid IE does not support
3143             // !important..
3144             
3145             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3146                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3147                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3148             } else {
3149                 bodyEl.dom.style.height = '';
3150                 bodyEl.dom.style.overflowY = '';
3151             }
3152             if (cw > w) {
3153                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3154             } else {
3155                 bodyEl.dom.style.overflowX = '';
3156             }
3157             
3158             dlg.setContentSize(w, bodyEl.getHeight());
3159             if(dlg.isVisible()){
3160                 dlg.fixedcenter = true;
3161             }
3162             return this;
3163         },
3164
3165         /**
3166          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3167          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3168          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3169          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3170          * @return {Roo.MessageBox} This message box
3171          */
3172         updateProgress : function(value, text){
3173             if(text){
3174                 this.updateText(text);
3175             }
3176             if (pp) { // weird bug on my firefox - for some reason this is not defined
3177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3178             }
3179             return this;
3180         },        
3181
3182         /**
3183          * Returns true if the message box is currently displayed
3184          * @return {Boolean} True if the message box is visible, else false
3185          */
3186         isVisible : function(){
3187             return dlg && dlg.isVisible();  
3188         },
3189
3190         /**
3191          * Hides the message box if it is displayed
3192          */
3193         hide : function(){
3194             if(this.isVisible()){
3195                 dlg.hide();
3196             }  
3197         },
3198
3199         /**
3200          * Displays a new message box, or reinitializes an existing message box, based on the config options
3201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3202          * The following config object properties are supported:
3203          * <pre>
3204 Property    Type             Description
3205 ----------  ---------------  ------------------------------------------------------------------------------------
3206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3207                                    closes (defaults to undefined)
3208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3211                                    progress and wait dialogs will ignore this property and always hide the
3212                                    close button as they can only be closed programmatically.
3213 cls               String           A custom CSS class to apply to the message box element
3214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3215                                    displayed (defaults to 75)
3216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3217                                    function will be btn (the name of the button that was clicked, if applicable,
3218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3219                                    Progress and wait dialogs will ignore this option since they do not respond to
3220                                    user actions and can only be closed programmatically, so any required function
3221                                    should be called by the same code after it closes the dialog.
3222 icon              String           A CSS class that provides a background image to be used as an icon for
3223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3226 modal             Boolean          False to allow user interaction with the page while the message box is
3227                                    displayed (defaults to true)
3228 msg               String           A string that will replace the existing message box body text (defaults
3229                                    to the XHTML-compliant non-breaking space character '&#160;')
3230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3231 progress          Boolean          True to display a progress bar (defaults to false)
3232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3235 title             String           The title text
3236 value             String           The string value to set into the active textbox element if displayed
3237 wait              Boolean          True to display a progress bar (defaults to false)
3238 width             Number           The width of the dialog in pixels
3239 </pre>
3240          *
3241          * Example usage:
3242          * <pre><code>
3243 Roo.Msg.show({
3244    title: 'Address',
3245    msg: 'Please enter your address:',
3246    width: 300,
3247    buttons: Roo.MessageBox.OKCANCEL,
3248    multiline: true,
3249    fn: saveAddress,
3250    animEl: 'addAddressBtn'
3251 });
3252 </code></pre>
3253          * @param {Object} config Configuration options
3254          * @return {Roo.MessageBox} This message box
3255          */
3256         show : function(options)
3257         {
3258             
3259             // this causes nightmares if you show one dialog after another
3260             // especially on callbacks..
3261              
3262             if(this.isVisible()){
3263                 
3264                 this.hide();
3265                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3266                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3267                 Roo.log("New Dialog Message:" +  options.msg )
3268                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3269                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3270                 
3271             }
3272             var d = this.getDialog();
3273             opt = options;
3274             d.setTitle(opt.title || "&#160;");
3275             d.closeEl.setDisplayed(opt.closable !== false);
3276             activeTextEl = textboxEl;
3277             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3278             if(opt.prompt){
3279                 if(opt.multiline){
3280                     textboxEl.hide();
3281                     textareaEl.show();
3282                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3283                         opt.multiline : this.defaultTextHeight);
3284                     activeTextEl = textareaEl;
3285                 }else{
3286                     textboxEl.show();
3287                     textareaEl.hide();
3288                 }
3289             }else{
3290                 textboxEl.hide();
3291                 textareaEl.hide();
3292             }
3293             progressEl.setDisplayed(opt.progress === true);
3294             this.updateProgress(0);
3295             activeTextEl.dom.value = opt.value || "";
3296             if(opt.prompt){
3297                 dlg.setDefaultButton(activeTextEl);
3298             }else{
3299                 var bs = opt.buttons;
3300                 var db = null;
3301                 if(bs && bs.ok){
3302                     db = buttons["ok"];
3303                 }else if(bs && bs.yes){
3304                     db = buttons["yes"];
3305                 }
3306                 dlg.setDefaultButton(db);
3307             }
3308             bwidth = updateButtons(opt.buttons);
3309             this.updateText(opt.msg);
3310             if(opt.cls){
3311                 d.el.addClass(opt.cls);
3312             }
3313             d.proxyDrag = opt.proxyDrag === true;
3314             d.modal = opt.modal !== false;
3315             d.mask = opt.modal !== false ? mask : false;
3316             if(!d.isVisible()){
3317                 // force it to the end of the z-index stack so it gets a cursor in FF
3318                 document.body.appendChild(dlg.el.dom);
3319                 d.animateTarget = null;
3320                 d.show(options.animEl);
3321             }
3322             return this;
3323         },
3324
3325         /**
3326          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3327          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3328          * and closing the message box when the process is complete.
3329          * @param {String} title The title bar text
3330          * @param {String} msg The message box body text
3331          * @return {Roo.MessageBox} This message box
3332          */
3333         progress : function(title, msg){
3334             this.show({
3335                 title : title,
3336                 msg : msg,
3337                 buttons: false,
3338                 progress:true,
3339                 closable:false,
3340                 minWidth: this.minProgressWidth,
3341                 modal : true
3342             });
3343             return this;
3344         },
3345
3346         /**
3347          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3348          * If a callback function is passed it will be called after the user clicks the button, and the
3349          * id of the button that was clicked will be passed as the only parameter to the callback
3350          * (could also be the top-right close button).
3351          * @param {String} title The title bar text
3352          * @param {String} msg The message box body text
3353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3354          * @param {Object} scope (optional) The scope of the callback function
3355          * @return {Roo.MessageBox} This message box
3356          */
3357         alert : function(title, msg, fn, scope)
3358         {
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.OK,
3363                 fn: fn,
3364                 closable : false,
3365                 scope : scope,
3366                 modal : true
3367             });
3368             return this;
3369         },
3370
3371         /**
3372          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3373          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3374          * You are responsible for closing the message box when the process is complete.
3375          * @param {String} msg The message box body text
3376          * @param {String} title (optional) The title bar text
3377          * @return {Roo.MessageBox} This message box
3378          */
3379         wait : function(msg, title){
3380             this.show({
3381                 title : title,
3382                 msg : msg,
3383                 buttons: false,
3384                 closable:false,
3385                 progress:true,
3386                 modal:true,
3387                 width:300,
3388                 wait:true
3389             });
3390             waitTimer = Roo.TaskMgr.start({
3391                 run: function(i){
3392                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3393                 },
3394                 interval: 1000
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3401          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3402          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3403          * @param {String} title The title bar text
3404          * @param {String} msg The message box body text
3405          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3406          * @param {Object} scope (optional) The scope of the callback function
3407          * @return {Roo.MessageBox} This message box
3408          */
3409         confirm : function(title, msg, fn, scope){
3410             this.show({
3411                 title : title,
3412                 msg : msg,
3413                 buttons: this.YESNO,
3414                 fn: fn,
3415                 scope : scope,
3416                 modal : true
3417             });
3418             return this;
3419         },
3420
3421         /**
3422          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3423          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3424          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3425          * (could also be the top-right close button) and the text that was entered will be passed as the two
3426          * parameters to the callback.
3427          * @param {String} title The title bar text
3428          * @param {String} msg The message box body text
3429          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3430          * @param {Object} scope (optional) The scope of the callback function
3431          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3432          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3433          * @return {Roo.MessageBox} This message box
3434          */
3435         prompt : function(title, msg, fn, scope, multiline){
3436             this.show({
3437                 title : title,
3438                 msg : msg,
3439                 buttons: this.OKCANCEL,
3440                 fn: fn,
3441                 minWidth:250,
3442                 scope : scope,
3443                 prompt:true,
3444                 multiline: multiline,
3445                 modal : true
3446             });
3447             return this;
3448         },
3449
3450         /**
3451          * Button config that displays a single OK button
3452          * @type Object
3453          */
3454         OK : {ok:true},
3455         /**
3456          * Button config that displays Yes and No buttons
3457          * @type Object
3458          */
3459         YESNO : {yes:true, no:true},
3460         /**
3461          * Button config that displays OK and Cancel buttons
3462          * @type Object
3463          */
3464         OKCANCEL : {ok:true, cancel:true},
3465         /**
3466          * Button config that displays Yes, No and Cancel buttons
3467          * @type Object
3468          */
3469         YESNOCANCEL : {yes:true, no:true, cancel:true},
3470
3471         /**
3472          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3473          * @type Number
3474          */
3475         defaultTextHeight : 75,
3476         /**
3477          * The maximum width in pixels of the message box (defaults to 600)
3478          * @type Number
3479          */
3480         maxWidth : 600,
3481         /**
3482          * The minimum width in pixels of the message box (defaults to 100)
3483          * @type Number
3484          */
3485         minWidth : 100,
3486         /**
3487          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3488          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3489          * @type Number
3490          */
3491         minProgressWidth : 250,
3492         /**
3493          * An object containing the default button text strings that can be overriden for localized language support.
3494          * Supported properties are: ok, cancel, yes and no.
3495          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3496          * @type Object
3497          */
3498         buttonText : {
3499             ok : "OK",
3500             cancel : "Cancel",
3501             yes : "Yes",
3502             no : "No"
3503         }
3504     };
3505 }();
3506
3507 /**
3508  * Shorthand for {@link Roo.MessageBox}
3509  */
3510 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3511 Roo.Msg = Roo.Msg || Roo.MessageBox;
3512 /*
3513  * - LGPL
3514  *
3515  * navbar
3516  * 
3517  */
3518
3519 /**
3520  * @class Roo.bootstrap.Navbar
3521  * @extends Roo.bootstrap.Component
3522  * Bootstrap Navbar class
3523
3524  * @constructor
3525  * Create a new Navbar
3526  * @param {Object} config The config object
3527  */
3528
3529
3530 Roo.bootstrap.Navbar = function(config){
3531     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event beforetoggle
3536          * Fire before toggle the menu
3537          * @param {Roo.EventObject} e
3538          */
3539         "beforetoggle" : true
3540     });
3541 };
3542
3543 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3544     
3545     
3546    
3547     // private
3548     navItems : false,
3549     loadMask : false,
3550     
3551     
3552     getAutoCreate : function(){
3553         
3554         
3555         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3556         
3557     },
3558     
3559     initEvents :function ()
3560     {
3561         //Roo.log(this.el.select('.navbar-toggle',true));
3562         this.el.select('.navbar-toggle',true).on('click', function() {
3563             if(this.fireEvent('beforetoggle', this) !== false){
3564                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3565             }
3566             
3567         }, this);
3568         
3569         var mark = {
3570             tag: "div",
3571             cls:"x-dlg-mask"
3572         };
3573         
3574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3575         
3576         var size = this.el.getSize();
3577         this.maskEl.setSize(size.width, size.height);
3578         this.maskEl.enableDisplayMode("block");
3579         this.maskEl.hide();
3580         
3581         if(this.loadMask){
3582             this.maskEl.show();
3583         }
3584     },
3585     
3586     
3587     getChildContainer : function()
3588     {
3589         if (this.el.select('.collapse').getCount()) {
3590             return this.el.select('.collapse',true).first();
3591         }
3592         
3593         return this.el;
3594     },
3595     
3596     mask : function()
3597     {
3598         this.maskEl.show();
3599     },
3600     
3601     unmask : function()
3602     {
3603         this.maskEl.hide();
3604     } 
3605     
3606     
3607     
3608     
3609 });
3610
3611
3612
3613  
3614
3615  /*
3616  * - LGPL
3617  *
3618  * navbar
3619  * 
3620  */
3621
3622 /**
3623  * @class Roo.bootstrap.NavSimplebar
3624  * @extends Roo.bootstrap.Navbar
3625  * Bootstrap Sidebar class
3626  *
3627  * @cfg {Boolean} inverse is inverted color
3628  * 
3629  * @cfg {String} type (nav | pills | tabs)
3630  * @cfg {Boolean} arrangement stacked | justified
3631  * @cfg {String} align (left | right) alignment
3632  * 
3633  * @cfg {Boolean} main (true|false) main nav bar? default false
3634  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3635  * 
3636  * @cfg {String} tag (header|footer|nav|div) default is nav 
3637
3638  * 
3639  * 
3640  * 
3641  * @constructor
3642  * Create a new Sidebar
3643  * @param {Object} config The config object
3644  */
3645
3646
3647 Roo.bootstrap.NavSimplebar = function(config){
3648     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3649 };
3650
3651 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3652     
3653     inverse: false,
3654     
3655     type: false,
3656     arrangement: '',
3657     align : false,
3658     
3659     
3660     
3661     main : false,
3662     
3663     
3664     tag : false,
3665     
3666     
3667     getAutoCreate : function(){
3668         
3669         
3670         var cfg = {
3671             tag : this.tag || 'div',
3672             cls : 'navbar'
3673         };
3674           
3675         
3676         cfg.cn = [
3677             {
3678                 cls: 'nav',
3679                 tag : 'ul'
3680             }
3681         ];
3682         
3683          
3684         this.type = this.type || 'nav';
3685         if (['tabs','pills'].indexOf(this.type)!==-1) {
3686             cfg.cn[0].cls += ' nav-' + this.type
3687         
3688         
3689         } else {
3690             if (this.type!=='nav') {
3691                 Roo.log('nav type must be nav/tabs/pills')
3692             }
3693             cfg.cn[0].cls += ' navbar-nav'
3694         }
3695         
3696         
3697         
3698         
3699         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3700             cfg.cn[0].cls += ' nav-' + this.arrangement;
3701         }
3702         
3703         
3704         if (this.align === 'right') {
3705             cfg.cn[0].cls += ' navbar-right';
3706         }
3707         
3708         if (this.inverse) {
3709             cfg.cls += ' navbar-inverse';
3710             
3711         }
3712         
3713         
3714         return cfg;
3715     
3716         
3717     }
3718     
3719     
3720     
3721 });
3722
3723
3724
3725  
3726
3727  
3728        /*
3729  * - LGPL
3730  *
3731  * navbar
3732  * 
3733  */
3734
3735 /**
3736  * @class Roo.bootstrap.NavHeaderbar
3737  * @extends Roo.bootstrap.NavSimplebar
3738  * Bootstrap Sidebar class
3739  *
3740  * @cfg {String} brand what is brand
3741  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3742  * @cfg {String} brand_href href of the brand
3743  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3744  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3745  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3746  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3747  * 
3748  * @constructor
3749  * Create a new Sidebar
3750  * @param {Object} config The config object
3751  */
3752
3753
3754 Roo.bootstrap.NavHeaderbar = function(config){
3755     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3756       
3757 };
3758
3759 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3760     
3761     position: '',
3762     brand: '',
3763     brand_href: false,
3764     srButton : true,
3765     autohide : false,
3766     desktopCenter : false,
3767    
3768     
3769     getAutoCreate : function(){
3770         
3771         var   cfg = {
3772             tag: this.nav || 'nav',
3773             cls: 'navbar',
3774             role: 'navigation',
3775             cn: []
3776         };
3777         
3778         var cn = cfg.cn;
3779         if (this.desktopCenter) {
3780             cn.push({cls : 'container', cn : []});
3781             cn = cn[0].cn;
3782         }
3783         
3784         if(this.srButton){
3785             cn.push({
3786                 tag: 'div',
3787                 cls: 'navbar-header',
3788                 cn: [
3789                     {
3790                         tag: 'button',
3791                         type: 'button',
3792                         cls: 'navbar-toggle',
3793                         'data-toggle': 'collapse',
3794                         cn: [
3795                             {
3796                                 tag: 'span',
3797                                 cls: 'sr-only',
3798                                 html: 'Toggle navigation'
3799                             },
3800                             {
3801                                 tag: 'span',
3802                                 cls: 'icon-bar'
3803                             },
3804                             {
3805                                 tag: 'span',
3806                                 cls: 'icon-bar'
3807                             },
3808                             {
3809                                 tag: 'span',
3810                                 cls: 'icon-bar'
3811                             }
3812                         ]
3813                     }
3814                 ]
3815             });
3816         }
3817         
3818         cn.push({
3819             tag: 'div',
3820             cls: 'collapse navbar-collapse',
3821             cn : []
3822         });
3823         
3824         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3825         
3826         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3827             cfg.cls += ' navbar-' + this.position;
3828             
3829             // tag can override this..
3830             
3831             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3832         }
3833         
3834         if (this.brand !== '') {
3835             cn[0].cn.push({
3836                 tag: 'a',
3837                 href: this.brand_href ? this.brand_href : '#',
3838                 cls: 'navbar-brand',
3839                 cn: [
3840                 this.brand
3841                 ]
3842             });
3843         }
3844         
3845         if(this.main){
3846             cfg.cls += ' main-nav';
3847         }
3848         
3849         
3850         return cfg;
3851
3852         
3853     },
3854     getHeaderChildContainer : function()
3855     {
3856         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3857             return this.el.select('.navbar-header',true).first();
3858         }
3859         
3860         return this.getChildContainer();
3861     },
3862     
3863     
3864     initEvents : function()
3865     {
3866         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3867         
3868         if (this.autohide) {
3869             
3870             var prevScroll = 0;
3871             var ft = this.el;
3872             
3873             Roo.get(document).on('scroll',function(e) {
3874                 var ns = Roo.get(document).getScroll().top;
3875                 var os = prevScroll;
3876                 prevScroll = ns;
3877                 
3878                 if(ns > os){
3879                     ft.removeClass('slideDown');
3880                     ft.addClass('slideUp');
3881                     return;
3882                 }
3883                 ft.removeClass('slideUp');
3884                 ft.addClass('slideDown');
3885                  
3886               
3887           },this);
3888         }
3889     }    
3890     
3891 });
3892
3893
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * navbar
3901  * 
3902  */
3903
3904 /**
3905  * @class Roo.bootstrap.NavSidebar
3906  * @extends Roo.bootstrap.Navbar
3907  * Bootstrap Sidebar class
3908  * 
3909  * @constructor
3910  * Create a new Sidebar
3911  * @param {Object} config The config object
3912  */
3913
3914
3915 Roo.bootstrap.NavSidebar = function(config){
3916     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3917 };
3918
3919 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3920     
3921     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3922     
3923     getAutoCreate : function(){
3924         
3925         
3926         return  {
3927             tag: 'div',
3928             cls: 'sidebar sidebar-nav'
3929         };
3930     
3931         
3932     }
3933     
3934     
3935     
3936 });
3937
3938
3939
3940  
3941
3942  /*
3943  * - LGPL
3944  *
3945  * nav group
3946  * 
3947  */
3948
3949 /**
3950  * @class Roo.bootstrap.NavGroup
3951  * @extends Roo.bootstrap.Component
3952  * Bootstrap NavGroup class
3953  * @cfg {String} align (left|right)
3954  * @cfg {Boolean} inverse
3955  * @cfg {String} type (nav|pills|tab) default nav
3956  * @cfg {String} navId - reference Id for navbar.
3957
3958  * 
3959  * @constructor
3960  * Create a new nav group
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.NavGroup = function(config){
3965     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3966     this.navItems = [];
3967    
3968     Roo.bootstrap.NavGroup.register(this);
3969      this.addEvents({
3970         /**
3971              * @event changed
3972              * Fires when the active item changes
3973              * @param {Roo.bootstrap.NavGroup} this
3974              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3975              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3976          */
3977         'changed': true
3978      });
3979     
3980 };
3981
3982 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3983     
3984     align: '',
3985     inverse: false,
3986     form: false,
3987     type: 'nav',
3988     navId : '',
3989     // private
3990     
3991     navItems : false, 
3992     
3993     getAutoCreate : function()
3994     {
3995         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3996         
3997         cfg = {
3998             tag : 'ul',
3999             cls: 'nav' 
4000         };
4001         
4002         if (['tabs','pills'].indexOf(this.type)!==-1) {
4003             cfg.cls += ' nav-' + this.type
4004         } else {
4005             if (this.type!=='nav') {
4006                 Roo.log('nav type must be nav/tabs/pills')
4007             }
4008             cfg.cls += ' navbar-nav'
4009         }
4010         
4011         if (this.parent().sidebar) {
4012             cfg = {
4013                 tag: 'ul',
4014                 cls: 'dashboard-menu sidebar-menu'
4015             };
4016             
4017             return cfg;
4018         }
4019         
4020         if (this.form === true) {
4021             cfg = {
4022                 tag: 'form',
4023                 cls: 'navbar-form'
4024             };
4025             
4026             if (this.align === 'right') {
4027                 cfg.cls += ' navbar-right';
4028             } else {
4029                 cfg.cls += ' navbar-left';
4030             }
4031         }
4032         
4033         if (this.align === 'right') {
4034             cfg.cls += ' navbar-right';
4035         }
4036         
4037         if (this.inverse) {
4038             cfg.cls += ' navbar-inverse';
4039             
4040         }
4041         
4042         
4043         return cfg;
4044     },
4045     /**
4046     * sets the active Navigation item
4047     * @param {Roo.bootstrap.NavItem} the new current navitem
4048     */
4049     setActiveItem : function(item)
4050     {
4051         var prev = false;
4052         Roo.each(this.navItems, function(v){
4053             if (v == item) {
4054                 return ;
4055             }
4056             if (v.isActive()) {
4057                 v.setActive(false, true);
4058                 prev = v;
4059                 
4060             }
4061             
4062         });
4063
4064         item.setActive(true, true);
4065         this.fireEvent('changed', this, item, prev);
4066         
4067         
4068     },
4069     /**
4070     * gets the active Navigation item
4071     * @return {Roo.bootstrap.NavItem} the current navitem
4072     */
4073     getActive : function()
4074     {
4075         
4076         var prev = false;
4077         Roo.each(this.navItems, function(v){
4078             
4079             if (v.isActive()) {
4080                 prev = v;
4081                 
4082             }
4083             
4084         });
4085         return prev;
4086     },
4087     
4088     indexOfNav : function()
4089     {
4090         
4091         var prev = false;
4092         Roo.each(this.navItems, function(v,i){
4093             
4094             if (v.isActive()) {
4095                 prev = i;
4096                 
4097             }
4098             
4099         });
4100         return prev;
4101     },
4102     /**
4103     * adds a Navigation item
4104     * @param {Roo.bootstrap.NavItem} the navitem to add
4105     */
4106     addItem : function(cfg)
4107     {
4108         var cn = new Roo.bootstrap.NavItem(cfg);
4109         this.register(cn);
4110         cn.parentId = this.id;
4111         cn.onRender(this.el, null);
4112         return cn;
4113     },
4114     /**
4115     * register a Navigation item
4116     * @param {Roo.bootstrap.NavItem} the navitem to add
4117     */
4118     register : function(item)
4119     {
4120         this.navItems.push( item);
4121         item.navId = this.navId;
4122     
4123     },
4124     
4125     /**
4126     * clear all the Navigation item
4127     */
4128    
4129     clearAll : function()
4130     {
4131         this.navItems = [];
4132         this.el.dom.innerHTML = '';
4133     },
4134     
4135     getNavItem: function(tabId)
4136     {
4137         var ret = false;
4138         Roo.each(this.navItems, function(e) {
4139             if (e.tabId == tabId) {
4140                ret =  e;
4141                return false;
4142             }
4143             return true;
4144             
4145         });
4146         return ret;
4147     },
4148     
4149     setActiveNext : function()
4150     {
4151         var i = this.indexOfNav(this.getActive());
4152         if (i > this.navItems.length) {
4153             return;
4154         }
4155         this.setActiveItem(this.navItems[i+1]);
4156     },
4157     setActivePrev : function()
4158     {
4159         var i = this.indexOfNav(this.getActive());
4160         if (i  < 1) {
4161             return;
4162         }
4163         this.setActiveItem(this.navItems[i-1]);
4164     },
4165     clearWasActive : function(except) {
4166         Roo.each(this.navItems, function(e) {
4167             if (e.tabId != except.tabId && e.was_active) {
4168                e.was_active = false;
4169                return false;
4170             }
4171             return true;
4172             
4173         });
4174     },
4175     getWasActive : function ()
4176     {
4177         var r = false;
4178         Roo.each(this.navItems, function(e) {
4179             if (e.was_active) {
4180                r = e;
4181                return false;
4182             }
4183             return true;
4184             
4185         });
4186         return r;
4187     }
4188     
4189     
4190 });
4191
4192  
4193 Roo.apply(Roo.bootstrap.NavGroup, {
4194     
4195     groups: {},
4196      /**
4197     * register a Navigation Group
4198     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4199     */
4200     register : function(navgrp)
4201     {
4202         this.groups[navgrp.navId] = navgrp;
4203         
4204     },
4205     /**
4206     * fetch a Navigation Group based on the navigation ID
4207     * @param {string} the navgroup to add
4208     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4209     */
4210     get: function(navId) {
4211         if (typeof(this.groups[navId]) == 'undefined') {
4212             return false;
4213             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4214         }
4215         return this.groups[navId] ;
4216     }
4217     
4218     
4219     
4220 });
4221
4222  /*
4223  * - LGPL
4224  *
4225  * row
4226  * 
4227  */
4228
4229 /**
4230  * @class Roo.bootstrap.NavItem
4231  * @extends Roo.bootstrap.Component
4232  * Bootstrap Navbar.NavItem class
4233  * @cfg {String} href  link to
4234  * @cfg {String} html content of button
4235  * @cfg {String} badge text inside badge
4236  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4237  * @cfg {String} glyphicon name of glyphicon
4238  * @cfg {String} icon name of font awesome icon
4239  * @cfg {Boolean} active Is item active
4240  * @cfg {Boolean} disabled Is item disabled
4241  
4242  * @cfg {Boolean} preventDefault (true | false) default false
4243  * @cfg {String} tabId the tab that this item activates.
4244  * @cfg {String} tagtype (a|span) render as a href or span?
4245  * @cfg {Boolean} animateRef (true|false) link to element default false  
4246   
4247  * @constructor
4248  * Create a new Navbar Item
4249  * @param {Object} config The config object
4250  */
4251 Roo.bootstrap.NavItem = function(config){
4252     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4253     this.addEvents({
4254         // raw events
4255         /**
4256          * @event click
4257          * The raw click event for the entire grid.
4258          * @param {Roo.EventObject} e
4259          */
4260         "click" : true,
4261          /**
4262             * @event changed
4263             * Fires when the active item active state changes
4264             * @param {Roo.bootstrap.NavItem} this
4265             * @param {boolean} state the new state
4266              
4267          */
4268         'changed': true,
4269         /**
4270             * @event scrollto
4271             * Fires when scroll to element
4272             * @param {Roo.bootstrap.NavItem} this
4273             * @param {Object} options
4274             * @param {Roo.EventObject} e
4275              
4276          */
4277         'scrollto': true
4278     });
4279    
4280 };
4281
4282 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4283     
4284     href: false,
4285     html: '',
4286     badge: '',
4287     icon: false,
4288     glyphicon: false,
4289     active: false,
4290     preventDefault : false,
4291     tabId : false,
4292     tagtype : 'a',
4293     disabled : false,
4294     animateRef : false,
4295     was_active : false,
4296     
4297     getAutoCreate : function(){
4298          
4299         var cfg = {
4300             tag: 'li',
4301             cls: 'nav-item'
4302             
4303         };
4304         
4305         if (this.active) {
4306             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4307         }
4308         if (this.disabled) {
4309             cfg.cls += ' disabled';
4310         }
4311         
4312         if (this.href || this.html || this.glyphicon || this.icon) {
4313             cfg.cn = [
4314                 {
4315                     tag: this.tagtype,
4316                     href : this.href || "#",
4317                     html: this.html || ''
4318                 }
4319             ];
4320             
4321             if (this.icon) {
4322                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4323             }
4324
4325             if(this.glyphicon) {
4326                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4327             }
4328             
4329             if (this.menu) {
4330                 
4331                 cfg.cn[0].html += " <span class='caret'></span>";
4332              
4333             }
4334             
4335             if (this.badge !== '') {
4336                  
4337                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4338             }
4339         }
4340         
4341         
4342         
4343         return cfg;
4344     },
4345     initEvents: function() 
4346     {
4347         if (typeof (this.menu) != 'undefined') {
4348             this.menu.parentType = this.xtype;
4349             this.menu.triggerEl = this.el;
4350             this.menu = this.addxtype(Roo.apply({}, this.menu));
4351         }
4352         
4353         this.el.select('a',true).on('click', this.onClick, this);
4354         
4355         if(this.tagtype == 'span'){
4356             this.el.select('span',true).on('click', this.onClick, this);
4357         }
4358        
4359         // at this point parent should be available..
4360         this.parent().register(this);
4361     },
4362     
4363     onClick : function(e)
4364     {
4365         if (e.getTarget('.dropdown-menu-item')) {
4366             // did you click on a menu itemm.... - then don't trigger onclick..
4367             return;
4368         }
4369         
4370         if(
4371                 this.preventDefault || 
4372                 this.href == '#' 
4373         ){
4374             Roo.log("NavItem - prevent Default?");
4375             e.preventDefault();
4376         }
4377         
4378         if (this.disabled) {
4379             return;
4380         }
4381         
4382         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4383         if (tg && tg.transition) {
4384             Roo.log("waiting for the transitionend");
4385             return;
4386         }
4387         
4388         
4389         
4390         //Roo.log("fire event clicked");
4391         if(this.fireEvent('click', this, e) === false){
4392             return;
4393         };
4394         
4395         if(this.tagtype == 'span'){
4396             return;
4397         }
4398         
4399         //Roo.log(this.href);
4400         var ael = this.el.select('a',true).first();
4401         //Roo.log(ael);
4402         
4403         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4404             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4405             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4406                 return; // ignore... - it's a 'hash' to another page.
4407             }
4408             Roo.log("NavItem - prevent Default?");
4409             e.preventDefault();
4410             this.scrollToElement(e);
4411         }
4412         
4413         
4414         var p =  this.parent();
4415    
4416         if (['tabs','pills'].indexOf(p.type)!==-1) {
4417             if (typeof(p.setActiveItem) !== 'undefined') {
4418                 p.setActiveItem(this);
4419             }
4420         }
4421         
4422         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4423         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4424             // remove the collapsed menu expand...
4425             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4426         }
4427     },
4428     
4429     isActive: function () {
4430         return this.active
4431     },
4432     setActive : function(state, fire, is_was_active)
4433     {
4434         if (this.active && !state && this.navId) {
4435             this.was_active = true;
4436             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4437             if (nv) {
4438                 nv.clearWasActive(this);
4439             }
4440             
4441         }
4442         this.active = state;
4443         
4444         if (!state ) {
4445             this.el.removeClass('active');
4446         } else if (!this.el.hasClass('active')) {
4447             this.el.addClass('active');
4448         }
4449         if (fire) {
4450             this.fireEvent('changed', this, state);
4451         }
4452         
4453         // show a panel if it's registered and related..
4454         
4455         if (!this.navId || !this.tabId || !state || is_was_active) {
4456             return;
4457         }
4458         
4459         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4460         if (!tg) {
4461             return;
4462         }
4463         var pan = tg.getPanelByName(this.tabId);
4464         if (!pan) {
4465             return;
4466         }
4467         // if we can not flip to new panel - go back to old nav highlight..
4468         if (false == tg.showPanel(pan)) {
4469             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4470             if (nv) {
4471                 var onav = nv.getWasActive();
4472                 if (onav) {
4473                     onav.setActive(true, false, true);
4474                 }
4475             }
4476             
4477         }
4478         
4479         
4480         
4481     },
4482      // this should not be here...
4483     setDisabled : function(state)
4484     {
4485         this.disabled = state;
4486         if (!state ) {
4487             this.el.removeClass('disabled');
4488         } else if (!this.el.hasClass('disabled')) {
4489             this.el.addClass('disabled');
4490         }
4491         
4492     },
4493     
4494     /**
4495      * Fetch the element to display the tooltip on.
4496      * @return {Roo.Element} defaults to this.el
4497      */
4498     tooltipEl : function()
4499     {
4500         return this.el.select('' + this.tagtype + '', true).first();
4501     },
4502     
4503     scrollToElement : function(e)
4504     {
4505         var c = document.body;
4506         
4507         /*
4508          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4509          */
4510         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4511             c = document.documentElement;
4512         }
4513         
4514         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4515         
4516         if(!target){
4517             return;
4518         }
4519
4520         var o = target.calcOffsetsTo(c);
4521         
4522         var options = {
4523             target : target,
4524             value : o[1]
4525         };
4526         
4527         this.fireEvent('scrollto', this, options, e);
4528         
4529         Roo.get(c).scrollTo('top', options.value, true);
4530         
4531         return;
4532     }
4533 });
4534  
4535
4536  /*
4537  * - LGPL
4538  *
4539  * sidebar item
4540  *
4541  *  li
4542  *    <span> icon </span>
4543  *    <span> text </span>
4544  *    <span>badge </span>
4545  */
4546
4547 /**
4548  * @class Roo.bootstrap.NavSidebarItem
4549  * @extends Roo.bootstrap.NavItem
4550  * Bootstrap Navbar.NavSidebarItem class
4551  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4552  * {bool} open is the menu open
4553  * @constructor
4554  * Create a new Navbar Button
4555  * @param {Object} config The config object
4556  */
4557 Roo.bootstrap.NavSidebarItem = function(config){
4558     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4559     this.addEvents({
4560         // raw events
4561         /**
4562          * @event click
4563          * The raw click event for the entire grid.
4564          * @param {Roo.EventObject} e
4565          */
4566         "click" : true,
4567          /**
4568             * @event changed
4569             * Fires when the active item active state changes
4570             * @param {Roo.bootstrap.NavSidebarItem} this
4571             * @param {boolean} state the new state
4572              
4573          */
4574         'changed': true
4575     });
4576    
4577 };
4578
4579 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4580     
4581     badgeWeight : 'default',
4582     
4583     open: false,
4584     
4585     getAutoCreate : function(){
4586         
4587         
4588         var a = {
4589                 tag: 'a',
4590                 href : this.href || '#',
4591                 cls: '',
4592                 html : '',
4593                 cn : []
4594         };
4595         var cfg = {
4596             tag: 'li',
4597             cls: '',
4598             cn: [ a ]
4599         };
4600         var span = {
4601             tag: 'span',
4602             html : this.html || ''
4603         };
4604         
4605         
4606         if (this.active) {
4607             cfg.cls += ' active';
4608         }
4609         
4610         if (this.disabled) {
4611             cfg.cls += ' disabled';
4612         }
4613         if (this.open) {
4614             cfg.cls += ' open x-open';
4615         }
4616         // left icon..
4617         if (this.glyphicon || this.icon) {
4618             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4619             a.cn.push({ tag : 'i', cls : c }) ;
4620         }
4621         // html..
4622         a.cn.push(span);
4623         // then badge..
4624         if (this.badge !== '') {
4625             
4626             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4627         }
4628         // fi
4629         if (this.menu) {
4630             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4631             a.cls += 'dropdown-toggle treeview' ;
4632         }
4633         
4634         return cfg;
4635          
4636            
4637     },
4638     
4639     initEvents : function()
4640     { 
4641         if (typeof (this.menu) != 'undefined') {
4642             this.menu.parentType = this.xtype;
4643             this.menu.triggerEl = this.el;
4644             this.menu = this.addxtype(Roo.apply({}, this.menu));
4645         }
4646         
4647         this.el.on('click', this.onClick, this);
4648        
4649     
4650         if(this.badge !== ''){
4651  
4652             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4653         }
4654         
4655     },
4656     
4657     onClick : function(e)
4658     {
4659         if(this.disabled){
4660             e.preventDefault();
4661             return;
4662         }
4663         
4664         if(this.preventDefault){
4665             e.preventDefault();
4666         }
4667         
4668         this.fireEvent('click', this);
4669     },
4670     
4671     disable : function()
4672     {
4673         this.setDisabled(true);
4674     },
4675     
4676     enable : function()
4677     {
4678         this.setDisabled(false);
4679     },
4680     
4681     setDisabled : function(state)
4682     {
4683         if(this.disabled == state){
4684             return;
4685         }
4686         
4687         this.disabled = state;
4688         
4689         if (state) {
4690             this.el.addClass('disabled');
4691             return;
4692         }
4693         
4694         this.el.removeClass('disabled');
4695         
4696         return;
4697     },
4698     
4699     setActive : function(state)
4700     {
4701         if(this.active == state){
4702             return;
4703         }
4704         
4705         this.active = state;
4706         
4707         if (state) {
4708             this.el.addClass('active');
4709             return;
4710         }
4711         
4712         this.el.removeClass('active');
4713         
4714         return;
4715     },
4716     
4717     isActive: function () 
4718     {
4719         return this.active;
4720     },
4721     
4722     setBadge : function(str)
4723     {
4724         if(!this.badgeEl){
4725             return;
4726         }
4727         
4728         this.badgeEl.dom.innerHTML = str;
4729     }
4730     
4731    
4732      
4733  
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * row
4741  * 
4742  */
4743
4744 /**
4745  * @class Roo.bootstrap.Row
4746  * @extends Roo.bootstrap.Component
4747  * Bootstrap Row class (contains columns...)
4748  * 
4749  * @constructor
4750  * Create a new Row
4751  * @param {Object} config The config object
4752  */
4753
4754 Roo.bootstrap.Row = function(config){
4755     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4756 };
4757
4758 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4759     
4760     getAutoCreate : function(){
4761        return {
4762             cls: 'row clearfix'
4763        };
4764     }
4765     
4766     
4767 });
4768
4769  
4770
4771  /*
4772  * - LGPL
4773  *
4774  * element
4775  * 
4776  */
4777
4778 /**
4779  * @class Roo.bootstrap.Element
4780  * @extends Roo.bootstrap.Component
4781  * Bootstrap Element class
4782  * @cfg {String} html contents of the element
4783  * @cfg {String} tag tag of the element
4784  * @cfg {String} cls class of the element
4785  * @cfg {Boolean} preventDefault (true|false) default false
4786  * @cfg {Boolean} clickable (true|false) default false
4787  * 
4788  * @constructor
4789  * Create a new Element
4790  * @param {Object} config The config object
4791  */
4792
4793 Roo.bootstrap.Element = function(config){
4794     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4795     
4796     this.addEvents({
4797         // raw events
4798         /**
4799          * @event click
4800          * When a element is chick
4801          * @param {Roo.bootstrap.Element} this
4802          * @param {Roo.EventObject} e
4803          */
4804         "click" : true
4805     });
4806 };
4807
4808 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4809     
4810     tag: 'div',
4811     cls: '',
4812     html: '',
4813     preventDefault: false, 
4814     clickable: false,
4815     
4816     getAutoCreate : function(){
4817         
4818         var cfg = {
4819             tag: this.tag,
4820             cls: this.cls,
4821             html: this.html
4822         };
4823         
4824         return cfg;
4825     },
4826     
4827     initEvents: function() 
4828     {
4829         Roo.bootstrap.Element.superclass.initEvents.call(this);
4830         
4831         if(this.clickable){
4832             this.el.on('click', this.onClick, this);
4833         }
4834         
4835     },
4836     
4837     onClick : function(e)
4838     {
4839         if(this.preventDefault){
4840             e.preventDefault();
4841         }
4842         
4843         this.fireEvent('click', this, e);
4844     },
4845     
4846     getValue : function()
4847     {
4848         return this.el.dom.innerHTML;
4849     },
4850     
4851     setValue : function(value)
4852     {
4853         this.el.dom.innerHTML = value;
4854     }
4855    
4856 });
4857
4858  
4859
4860  /*
4861  * - LGPL
4862  *
4863  * pagination
4864  * 
4865  */
4866
4867 /**
4868  * @class Roo.bootstrap.Pagination
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap Pagination class
4871  * @cfg {String} size xs | sm | md | lg
4872  * @cfg {Boolean} inverse false | true
4873  * 
4874  * @constructor
4875  * Create a new Pagination
4876  * @param {Object} config The config object
4877  */
4878
4879 Roo.bootstrap.Pagination = function(config){
4880     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4881 };
4882
4883 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4884     
4885     cls: false,
4886     size: false,
4887     inverse: false,
4888     
4889     getAutoCreate : function(){
4890         var cfg = {
4891             tag: 'ul',
4892                 cls: 'pagination'
4893         };
4894         if (this.inverse) {
4895             cfg.cls += ' inverse';
4896         }
4897         if (this.html) {
4898             cfg.html=this.html;
4899         }
4900         if (this.cls) {
4901             cfg.cls += " " + this.cls;
4902         }
4903         return cfg;
4904     }
4905    
4906 });
4907
4908  
4909
4910  /*
4911  * - LGPL
4912  *
4913  * Pagination item
4914  * 
4915  */
4916
4917
4918 /**
4919  * @class Roo.bootstrap.PaginationItem
4920  * @extends Roo.bootstrap.Component
4921  * Bootstrap PaginationItem class
4922  * @cfg {String} html text
4923  * @cfg {String} href the link
4924  * @cfg {Boolean} preventDefault (true | false) default true
4925  * @cfg {Boolean} active (true | false) default false
4926  * @cfg {Boolean} disabled default false
4927  * 
4928  * 
4929  * @constructor
4930  * Create a new PaginationItem
4931  * @param {Object} config The config object
4932  */
4933
4934
4935 Roo.bootstrap.PaginationItem = function(config){
4936     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4937     this.addEvents({
4938         // raw events
4939         /**
4940          * @event click
4941          * The raw click event for the entire grid.
4942          * @param {Roo.EventObject} e
4943          */
4944         "click" : true
4945     });
4946 };
4947
4948 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4949     
4950     href : false,
4951     html : false,
4952     preventDefault: true,
4953     active : false,
4954     cls : false,
4955     disabled: false,
4956     
4957     getAutoCreate : function(){
4958         var cfg= {
4959             tag: 'li',
4960             cn: [
4961                 {
4962                     tag : 'a',
4963                     href : this.href ? this.href : '#',
4964                     html : this.html ? this.html : ''
4965                 }
4966             ]
4967         };
4968         
4969         if(this.cls){
4970             cfg.cls = this.cls;
4971         }
4972         
4973         if(this.disabled){
4974             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4975         }
4976         
4977         if(this.active){
4978             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4979         }
4980         
4981         return cfg;
4982     },
4983     
4984     initEvents: function() {
4985         
4986         this.el.on('click', this.onClick, this);
4987         
4988     },
4989     onClick : function(e)
4990     {
4991         Roo.log('PaginationItem on click ');
4992         if(this.preventDefault){
4993             e.preventDefault();
4994         }
4995         
4996         if(this.disabled){
4997             return;
4998         }
4999         
5000         this.fireEvent('click', this, e);
5001     }
5002    
5003 });
5004
5005  
5006
5007  /*
5008  * - LGPL
5009  *
5010  * slider
5011  * 
5012  */
5013
5014
5015 /**
5016  * @class Roo.bootstrap.Slider
5017  * @extends Roo.bootstrap.Component
5018  * Bootstrap Slider class
5019  *    
5020  * @constructor
5021  * Create a new Slider
5022  * @param {Object} config The config object
5023  */
5024
5025 Roo.bootstrap.Slider = function(config){
5026     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5027 };
5028
5029 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5030     
5031     getAutoCreate : function(){
5032         
5033         var cfg = {
5034             tag: 'div',
5035             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5036             cn: [
5037                 {
5038                     tag: 'a',
5039                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5040                 }
5041             ]
5042         };
5043         
5044         return cfg;
5045     }
5046    
5047 });
5048
5049  /*
5050  * Based on:
5051  * Ext JS Library 1.1.1
5052  * Copyright(c) 2006-2007, Ext JS, LLC.
5053  *
5054  * Originally Released Under LGPL - original licence link has changed is not relivant.
5055  *
5056  * Fork - LGPL
5057  * <script type="text/javascript">
5058  */
5059  
5060
5061 /**
5062  * @class Roo.grid.ColumnModel
5063  * @extends Roo.util.Observable
5064  * This is the default implementation of a ColumnModel used by the Grid. It defines
5065  * the columns in the grid.
5066  * <br>Usage:<br>
5067  <pre><code>
5068  var colModel = new Roo.grid.ColumnModel([
5069         {header: "Ticker", width: 60, sortable: true, locked: true},
5070         {header: "Company Name", width: 150, sortable: true},
5071         {header: "Market Cap.", width: 100, sortable: true},
5072         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5073         {header: "Employees", width: 100, sortable: true, resizable: false}
5074  ]);
5075  </code></pre>
5076  * <p>
5077  
5078  * The config options listed for this class are options which may appear in each
5079  * individual column definition.
5080  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5081  * @constructor
5082  * @param {Object} config An Array of column config objects. See this class's
5083  * config objects for details.
5084 */
5085 Roo.grid.ColumnModel = function(config){
5086         /**
5087      * The config passed into the constructor
5088      */
5089     this.config = config;
5090     this.lookup = {};
5091
5092     // if no id, create one
5093     // if the column does not have a dataIndex mapping,
5094     // map it to the order it is in the config
5095     for(var i = 0, len = config.length; i < len; i++){
5096         var c = config[i];
5097         if(typeof c.dataIndex == "undefined"){
5098             c.dataIndex = i;
5099         }
5100         if(typeof c.renderer == "string"){
5101             c.renderer = Roo.util.Format[c.renderer];
5102         }
5103         if(typeof c.id == "undefined"){
5104             c.id = Roo.id();
5105         }
5106         if(c.editor && c.editor.xtype){
5107             c.editor  = Roo.factory(c.editor, Roo.grid);
5108         }
5109         if(c.editor && c.editor.isFormField){
5110             c.editor = new Roo.grid.GridEditor(c.editor);
5111         }
5112         this.lookup[c.id] = c;
5113     }
5114
5115     /**
5116      * The width of columns which have no width specified (defaults to 100)
5117      * @type Number
5118      */
5119     this.defaultWidth = 100;
5120
5121     /**
5122      * Default sortable of columns which have no sortable specified (defaults to false)
5123      * @type Boolean
5124      */
5125     this.defaultSortable = false;
5126
5127     this.addEvents({
5128         /**
5129              * @event widthchange
5130              * Fires when the width of a column changes.
5131              * @param {ColumnModel} this
5132              * @param {Number} columnIndex The column index
5133              * @param {Number} newWidth The new width
5134              */
5135             "widthchange": true,
5136         /**
5137              * @event headerchange
5138              * Fires when the text of a header changes.
5139              * @param {ColumnModel} this
5140              * @param {Number} columnIndex The column index
5141              * @param {Number} newText The new header text
5142              */
5143             "headerchange": true,
5144         /**
5145              * @event hiddenchange
5146              * Fires when a column is hidden or "unhidden".
5147              * @param {ColumnModel} this
5148              * @param {Number} columnIndex The column index
5149              * @param {Boolean} hidden true if hidden, false otherwise
5150              */
5151             "hiddenchange": true,
5152             /**
5153          * @event columnmoved
5154          * Fires when a column is moved.
5155          * @param {ColumnModel} this
5156          * @param {Number} oldIndex
5157          * @param {Number} newIndex
5158          */
5159         "columnmoved" : true,
5160         /**
5161          * @event columlockchange
5162          * Fires when a column's locked state is changed
5163          * @param {ColumnModel} this
5164          * @param {Number} colIndex
5165          * @param {Boolean} locked true if locked
5166          */
5167         "columnlockchange" : true
5168     });
5169     Roo.grid.ColumnModel.superclass.constructor.call(this);
5170 };
5171 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5172     /**
5173      * @cfg {String} header The header text to display in the Grid view.
5174      */
5175     /**
5176      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5177      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5178      * specified, the column's index is used as an index into the Record's data Array.
5179      */
5180     /**
5181      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5182      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5183      */
5184     /**
5185      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5186      * Defaults to the value of the {@link #defaultSortable} property.
5187      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5188      */
5189     /**
5190      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5191      */
5192     /**
5193      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5194      */
5195     /**
5196      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5197      */
5198     /**
5199      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5200      */
5201     /**
5202      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5203      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5204      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5205      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5206      */
5207        /**
5208      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5209      */
5210     /**
5211      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5212      */
5213     /**
5214      * @cfg {String} cursor (Optional)
5215      */
5216     /**
5217      * @cfg {String} tooltip (Optional)
5218      */
5219     /**
5220      * @cfg {Number} xs (Optional)
5221      */
5222     /**
5223      * @cfg {Number} sm (Optional)
5224      */
5225     /**
5226      * @cfg {Number} md (Optional)
5227      */
5228     /**
5229      * @cfg {Number} lg (Optional)
5230      */
5231     /**
5232      * Returns the id of the column at the specified index.
5233      * @param {Number} index The column index
5234      * @return {String} the id
5235      */
5236     getColumnId : function(index){
5237         return this.config[index].id;
5238     },
5239
5240     /**
5241      * Returns the column for a specified id.
5242      * @param {String} id The column id
5243      * @return {Object} the column
5244      */
5245     getColumnById : function(id){
5246         return this.lookup[id];
5247     },
5248
5249     
5250     /**
5251      * Returns the column for a specified dataIndex.
5252      * @param {String} dataIndex The column dataIndex
5253      * @return {Object|Boolean} the column or false if not found
5254      */
5255     getColumnByDataIndex: function(dataIndex){
5256         var index = this.findColumnIndex(dataIndex);
5257         return index > -1 ? this.config[index] : false;
5258     },
5259     
5260     /**
5261      * Returns the index for a specified column id.
5262      * @param {String} id The column id
5263      * @return {Number} the index, or -1 if not found
5264      */
5265     getIndexById : function(id){
5266         for(var i = 0, len = this.config.length; i < len; i++){
5267             if(this.config[i].id == id){
5268                 return i;
5269             }
5270         }
5271         return -1;
5272     },
5273     
5274     /**
5275      * Returns the index for a specified column dataIndex.
5276      * @param {String} dataIndex The column dataIndex
5277      * @return {Number} the index, or -1 if not found
5278      */
5279     
5280     findColumnIndex : function(dataIndex){
5281         for(var i = 0, len = this.config.length; i < len; i++){
5282             if(this.config[i].dataIndex == dataIndex){
5283                 return i;
5284             }
5285         }
5286         return -1;
5287     },
5288     
5289     
5290     moveColumn : function(oldIndex, newIndex){
5291         var c = this.config[oldIndex];
5292         this.config.splice(oldIndex, 1);
5293         this.config.splice(newIndex, 0, c);
5294         this.dataMap = null;
5295         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5296     },
5297
5298     isLocked : function(colIndex){
5299         return this.config[colIndex].locked === true;
5300     },
5301
5302     setLocked : function(colIndex, value, suppressEvent){
5303         if(this.isLocked(colIndex) == value){
5304             return;
5305         }
5306         this.config[colIndex].locked = value;
5307         if(!suppressEvent){
5308             this.fireEvent("columnlockchange", this, colIndex, value);
5309         }
5310     },
5311
5312     getTotalLockedWidth : function(){
5313         var totalWidth = 0;
5314         for(var i = 0; i < this.config.length; i++){
5315             if(this.isLocked(i) && !this.isHidden(i)){
5316                 this.totalWidth += this.getColumnWidth(i);
5317             }
5318         }
5319         return totalWidth;
5320     },
5321
5322     getLockedCount : function(){
5323         for(var i = 0, len = this.config.length; i < len; i++){
5324             if(!this.isLocked(i)){
5325                 return i;
5326             }
5327         }
5328         
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the number of columns.
5334      * @return {Number}
5335      */
5336     getColumnCount : function(visibleOnly){
5337         if(visibleOnly === true){
5338             var c = 0;
5339             for(var i = 0, len = this.config.length; i < len; i++){
5340                 if(!this.isHidden(i)){
5341                     c++;
5342                 }
5343             }
5344             return c;
5345         }
5346         return this.config.length;
5347     },
5348
5349     /**
5350      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5351      * @param {Function} fn
5352      * @param {Object} scope (optional)
5353      * @return {Array} result
5354      */
5355     getColumnsBy : function(fn, scope){
5356         var r = [];
5357         for(var i = 0, len = this.config.length; i < len; i++){
5358             var c = this.config[i];
5359             if(fn.call(scope||this, c, i) === true){
5360                 r[r.length] = c;
5361             }
5362         }
5363         return r;
5364     },
5365
5366     /**
5367      * Returns true if the specified column is sortable.
5368      * @param {Number} col The column index
5369      * @return {Boolean}
5370      */
5371     isSortable : function(col){
5372         if(typeof this.config[col].sortable == "undefined"){
5373             return this.defaultSortable;
5374         }
5375         return this.config[col].sortable;
5376     },
5377
5378     /**
5379      * Returns the rendering (formatting) function defined for the column.
5380      * @param {Number} col The column index.
5381      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5382      */
5383     getRenderer : function(col){
5384         if(!this.config[col].renderer){
5385             return Roo.grid.ColumnModel.defaultRenderer;
5386         }
5387         return this.config[col].renderer;
5388     },
5389
5390     /**
5391      * Sets the rendering (formatting) function for a column.
5392      * @param {Number} col The column index
5393      * @param {Function} fn The function to use to process the cell's raw data
5394      * to return HTML markup for the grid view. The render function is called with
5395      * the following parameters:<ul>
5396      * <li>Data value.</li>
5397      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5398      * <li>css A CSS style string to apply to the table cell.</li>
5399      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5400      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5401      * <li>Row index</li>
5402      * <li>Column index</li>
5403      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5404      */
5405     setRenderer : function(col, fn){
5406         this.config[col].renderer = fn;
5407     },
5408
5409     /**
5410      * Returns the width for the specified column.
5411      * @param {Number} col The column index
5412      * @return {Number}
5413      */
5414     getColumnWidth : function(col){
5415         return this.config[col].width * 1 || this.defaultWidth;
5416     },
5417
5418     /**
5419      * Sets the width for a column.
5420      * @param {Number} col The column index
5421      * @param {Number} width The new width
5422      */
5423     setColumnWidth : function(col, width, suppressEvent){
5424         this.config[col].width = width;
5425         this.totalWidth = null;
5426         if(!suppressEvent){
5427              this.fireEvent("widthchange", this, col, width);
5428         }
5429     },
5430
5431     /**
5432      * Returns the total width of all columns.
5433      * @param {Boolean} includeHidden True to include hidden column widths
5434      * @return {Number}
5435      */
5436     getTotalWidth : function(includeHidden){
5437         if(!this.totalWidth){
5438             this.totalWidth = 0;
5439             for(var i = 0, len = this.config.length; i < len; i++){
5440                 if(includeHidden || !this.isHidden(i)){
5441                     this.totalWidth += this.getColumnWidth(i);
5442                 }
5443             }
5444         }
5445         return this.totalWidth;
5446     },
5447
5448     /**
5449      * Returns the header for the specified column.
5450      * @param {Number} col The column index
5451      * @return {String}
5452      */
5453     getColumnHeader : function(col){
5454         return this.config[col].header;
5455     },
5456
5457     /**
5458      * Sets the header for a column.
5459      * @param {Number} col The column index
5460      * @param {String} header The new header
5461      */
5462     setColumnHeader : function(col, header){
5463         this.config[col].header = header;
5464         this.fireEvent("headerchange", this, col, header);
5465     },
5466
5467     /**
5468      * Returns the tooltip for the specified column.
5469      * @param {Number} col The column index
5470      * @return {String}
5471      */
5472     getColumnTooltip : function(col){
5473             return this.config[col].tooltip;
5474     },
5475     /**
5476      * Sets the tooltip for a column.
5477      * @param {Number} col The column index
5478      * @param {String} tooltip The new tooltip
5479      */
5480     setColumnTooltip : function(col, tooltip){
5481             this.config[col].tooltip = tooltip;
5482     },
5483
5484     /**
5485      * Returns the dataIndex for the specified column.
5486      * @param {Number} col The column index
5487      * @return {Number}
5488      */
5489     getDataIndex : function(col){
5490         return this.config[col].dataIndex;
5491     },
5492
5493     /**
5494      * Sets the dataIndex for a column.
5495      * @param {Number} col The column index
5496      * @param {Number} dataIndex The new dataIndex
5497      */
5498     setDataIndex : function(col, dataIndex){
5499         this.config[col].dataIndex = dataIndex;
5500     },
5501
5502     
5503     
5504     /**
5505      * Returns true if the cell is editable.
5506      * @param {Number} colIndex The column index
5507      * @param {Number} rowIndex The row index - this is nto actually used..?
5508      * @return {Boolean}
5509      */
5510     isCellEditable : function(colIndex, rowIndex){
5511         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5512     },
5513
5514     /**
5515      * Returns the editor defined for the cell/column.
5516      * return false or null to disable editing.
5517      * @param {Number} colIndex The column index
5518      * @param {Number} rowIndex The row index
5519      * @return {Object}
5520      */
5521     getCellEditor : function(colIndex, rowIndex){
5522         return this.config[colIndex].editor;
5523     },
5524
5525     /**
5526      * Sets if a column is editable.
5527      * @param {Number} col The column index
5528      * @param {Boolean} editable True if the column is editable
5529      */
5530     setEditable : function(col, editable){
5531         this.config[col].editable = editable;
5532     },
5533
5534
5535     /**
5536      * Returns true if the column is hidden.
5537      * @param {Number} colIndex The column index
5538      * @return {Boolean}
5539      */
5540     isHidden : function(colIndex){
5541         return this.config[colIndex].hidden;
5542     },
5543
5544
5545     /**
5546      * Returns true if the column width cannot be changed
5547      */
5548     isFixed : function(colIndex){
5549         return this.config[colIndex].fixed;
5550     },
5551
5552     /**
5553      * Returns true if the column can be resized
5554      * @return {Boolean}
5555      */
5556     isResizable : function(colIndex){
5557         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5558     },
5559     /**
5560      * Sets if a column is hidden.
5561      * @param {Number} colIndex The column index
5562      * @param {Boolean} hidden True if the column is hidden
5563      */
5564     setHidden : function(colIndex, hidden){
5565         this.config[colIndex].hidden = hidden;
5566         this.totalWidth = null;
5567         this.fireEvent("hiddenchange", this, colIndex, hidden);
5568     },
5569
5570     /**
5571      * Sets the editor for a column.
5572      * @param {Number} col The column index
5573      * @param {Object} editor The editor object
5574      */
5575     setEditor : function(col, editor){
5576         this.config[col].editor = editor;
5577     }
5578 });
5579
5580 Roo.grid.ColumnModel.defaultRenderer = function(value)
5581 {
5582     if(typeof value == "object") {
5583         return value;
5584     }
5585         if(typeof value == "string" && value.length < 1){
5586             return "&#160;";
5587         }
5588     
5589         return String.format("{0}", value);
5590 };
5591
5592 // Alias for backwards compatibility
5593 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5594 /*
5595  * Based on:
5596  * Ext JS Library 1.1.1
5597  * Copyright(c) 2006-2007, Ext JS, LLC.
5598  *
5599  * Originally Released Under LGPL - original licence link has changed is not relivant.
5600  *
5601  * Fork - LGPL
5602  * <script type="text/javascript">
5603  */
5604  
5605 /**
5606  * @class Roo.LoadMask
5607  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5608  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5609  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5610  * element's UpdateManager load indicator and will be destroyed after the initial load.
5611  * @constructor
5612  * Create a new LoadMask
5613  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5614  * @param {Object} config The config object
5615  */
5616 Roo.LoadMask = function(el, config){
5617     this.el = Roo.get(el);
5618     Roo.apply(this, config);
5619     if(this.store){
5620         this.store.on('beforeload', this.onBeforeLoad, this);
5621         this.store.on('load', this.onLoad, this);
5622         this.store.on('loadexception', this.onLoadException, this);
5623         this.removeMask = false;
5624     }else{
5625         var um = this.el.getUpdateManager();
5626         um.showLoadIndicator = false; // disable the default indicator
5627         um.on('beforeupdate', this.onBeforeLoad, this);
5628         um.on('update', this.onLoad, this);
5629         um.on('failure', this.onLoad, this);
5630         this.removeMask = true;
5631     }
5632 };
5633
5634 Roo.LoadMask.prototype = {
5635     /**
5636      * @cfg {Boolean} removeMask
5637      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5638      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5639      */
5640     /**
5641      * @cfg {String} msg
5642      * The text to display in a centered loading message box (defaults to 'Loading...')
5643      */
5644     msg : 'Loading...',
5645     /**
5646      * @cfg {String} msgCls
5647      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5648      */
5649     msgCls : 'x-mask-loading',
5650
5651     /**
5652      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5653      * @type Boolean
5654      */
5655     disabled: false,
5656
5657     /**
5658      * Disables the mask to prevent it from being displayed
5659      */
5660     disable : function(){
5661        this.disabled = true;
5662     },
5663
5664     /**
5665      * Enables the mask so that it can be displayed
5666      */
5667     enable : function(){
5668         this.disabled = false;
5669     },
5670     
5671     onLoadException : function()
5672     {
5673         Roo.log(arguments);
5674         
5675         if (typeof(arguments[3]) != 'undefined') {
5676             Roo.MessageBox.alert("Error loading",arguments[3]);
5677         } 
5678         /*
5679         try {
5680             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5681                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5682             }   
5683         } catch(e) {
5684             
5685         }
5686         */
5687     
5688         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5689     },
5690     // private
5691     onLoad : function()
5692     {
5693         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5694     },
5695
5696     // private
5697     onBeforeLoad : function(){
5698         if(!this.disabled){
5699             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5700         }
5701     },
5702
5703     // private
5704     destroy : function(){
5705         if(this.store){
5706             this.store.un('beforeload', this.onBeforeLoad, this);
5707             this.store.un('load', this.onLoad, this);
5708             this.store.un('loadexception', this.onLoadException, this);
5709         }else{
5710             var um = this.el.getUpdateManager();
5711             um.un('beforeupdate', this.onBeforeLoad, this);
5712             um.un('update', this.onLoad, this);
5713             um.un('failure', this.onLoad, this);
5714         }
5715     }
5716 };/*
5717  * - LGPL
5718  *
5719  * table
5720  * 
5721  */
5722
5723 /**
5724  * @class Roo.bootstrap.Table
5725  * @extends Roo.bootstrap.Component
5726  * Bootstrap Table class
5727  * @cfg {String} cls table class
5728  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5729  * @cfg {String} bgcolor Specifies the background color for a table
5730  * @cfg {Number} border Specifies whether the table cells should have borders or not
5731  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5732  * @cfg {Number} cellspacing Specifies the space between cells
5733  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5734  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5735  * @cfg {String} sortable Specifies that the table should be sortable
5736  * @cfg {String} summary Specifies a summary of the content of a table
5737  * @cfg {Number} width Specifies the width of a table
5738  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5739  * 
5740  * @cfg {boolean} striped Should the rows be alternative striped
5741  * @cfg {boolean} bordered Add borders to the table
5742  * @cfg {boolean} hover Add hover highlighting
5743  * @cfg {boolean} condensed Format condensed
5744  * @cfg {boolean} responsive Format condensed
5745  * @cfg {Boolean} loadMask (true|false) default false
5746  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5747  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5748  * @cfg {Boolean} rowSelection (true|false) default false
5749  * @cfg {Boolean} cellSelection (true|false) default false
5750  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5751  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5752  
5753  * 
5754  * @constructor
5755  * Create a new Table
5756  * @param {Object} config The config object
5757  */
5758
5759 Roo.bootstrap.Table = function(config){
5760     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5761     
5762   
5763     
5764     // BC...
5765     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5766     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5767     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5768     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5769     
5770     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5771     if (this.sm) {
5772         this.sm.grid = this;
5773         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5774         this.sm = this.selModel;
5775         this.sm.xmodule = this.xmodule || false;
5776     }
5777     
5778     if (this.cm && typeof(this.cm.config) == 'undefined') {
5779         this.colModel = new Roo.grid.ColumnModel(this.cm);
5780         this.cm = this.colModel;
5781         this.cm.xmodule = this.xmodule || false;
5782     }
5783     if (this.store) {
5784         this.store= Roo.factory(this.store, Roo.data);
5785         this.ds = this.store;
5786         this.ds.xmodule = this.xmodule || false;
5787          
5788     }
5789     if (this.footer && this.store) {
5790         this.footer.dataSource = this.ds;
5791         this.footer = Roo.factory(this.footer);
5792     }
5793     
5794     /** @private */
5795     this.addEvents({
5796         /**
5797          * @event cellclick
5798          * Fires when a cell is clicked
5799          * @param {Roo.bootstrap.Table} this
5800          * @param {Roo.Element} el
5801          * @param {Number} rowIndex
5802          * @param {Number} columnIndex
5803          * @param {Roo.EventObject} e
5804          */
5805         "cellclick" : true,
5806         /**
5807          * @event celldblclick
5808          * Fires when a cell is double clicked
5809          * @param {Roo.bootstrap.Table} this
5810          * @param {Roo.Element} el
5811          * @param {Number} rowIndex
5812          * @param {Number} columnIndex
5813          * @param {Roo.EventObject} e
5814          */
5815         "celldblclick" : true,
5816         /**
5817          * @event rowclick
5818          * Fires when a row is clicked
5819          * @param {Roo.bootstrap.Table} this
5820          * @param {Roo.Element} el
5821          * @param {Number} rowIndex
5822          * @param {Roo.EventObject} e
5823          */
5824         "rowclick" : true,
5825         /**
5826          * @event rowdblclick
5827          * Fires when a row is double clicked
5828          * @param {Roo.bootstrap.Table} this
5829          * @param {Roo.Element} el
5830          * @param {Number} rowIndex
5831          * @param {Roo.EventObject} e
5832          */
5833         "rowdblclick" : true,
5834         /**
5835          * @event mouseover
5836          * Fires when a mouseover occur
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Roo.Element} el
5839          * @param {Number} rowIndex
5840          * @param {Number} columnIndex
5841          * @param {Roo.EventObject} e
5842          */
5843         "mouseover" : true,
5844         /**
5845          * @event mouseout
5846          * Fires when a mouseout occur
5847          * @param {Roo.bootstrap.Table} this
5848          * @param {Roo.Element} el
5849          * @param {Number} rowIndex
5850          * @param {Number} columnIndex
5851          * @param {Roo.EventObject} e
5852          */
5853         "mouseout" : true,
5854         /**
5855          * @event rowclass
5856          * Fires when a row is rendered, so you can change add a style to it.
5857          * @param {Roo.bootstrap.Table} this
5858          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5859          */
5860         'rowclass' : true,
5861           /**
5862          * @event rowsrendered
5863          * Fires when all the  rows have been rendered
5864          * @param {Roo.bootstrap.Table} this
5865          */
5866         'rowsrendered' : true,
5867         /**
5868          * @event contextmenu
5869          * The raw contextmenu event for the entire grid.
5870          * @param {Roo.EventObject} e
5871          */
5872         "contextmenu" : true,
5873         /**
5874          * @event rowcontextmenu
5875          * Fires when a row is right clicked
5876          * @param {Roo.bootstrap.Table} this
5877          * @param {Number} rowIndex
5878          * @param {Roo.EventObject} e
5879          */
5880         "rowcontextmenu" : true,
5881         /**
5882          * @event cellcontextmenu
5883          * Fires when a cell is right clicked
5884          * @param {Roo.bootstrap.Table} this
5885          * @param {Number} rowIndex
5886          * @param {Number} cellIndex
5887          * @param {Roo.EventObject} e
5888          */
5889          "cellcontextmenu" : true,
5890          /**
5891          * @event headercontextmenu
5892          * Fires when a header is right clicked
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Number} columnIndex
5895          * @param {Roo.EventObject} e
5896          */
5897         "headercontextmenu" : true
5898     });
5899 };
5900
5901 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5902     
5903     cls: false,
5904     align: false,
5905     bgcolor: false,
5906     border: false,
5907     cellpadding: false,
5908     cellspacing: false,
5909     frame: false,
5910     rules: false,
5911     sortable: false,
5912     summary: false,
5913     width: false,
5914     striped : false,
5915     scrollBody : false,
5916     bordered: false,
5917     hover:  false,
5918     condensed : false,
5919     responsive : false,
5920     sm : false,
5921     cm : false,
5922     store : false,
5923     loadMask : false,
5924     footerShow : true,
5925     headerShow : true,
5926   
5927     rowSelection : false,
5928     cellSelection : false,
5929     layout : false,
5930     
5931     // Roo.Element - the tbody
5932     mainBody: false,
5933     // Roo.Element - thead element
5934     mainHead: false,
5935     
5936     container: false, // used by gridpanel...
5937     
5938     getAutoCreate : function()
5939     {
5940         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5941         
5942         cfg = {
5943             tag: 'table',
5944             cls : 'table',
5945             cn : []
5946         };
5947         if (this.scrollBody) {
5948             cfg.cls += ' table-body-fixed';
5949         }    
5950         if (this.striped) {
5951             cfg.cls += ' table-striped';
5952         }
5953         
5954         if (this.hover) {
5955             cfg.cls += ' table-hover';
5956         }
5957         if (this.bordered) {
5958             cfg.cls += ' table-bordered';
5959         }
5960         if (this.condensed) {
5961             cfg.cls += ' table-condensed';
5962         }
5963         if (this.responsive) {
5964             cfg.cls += ' table-responsive';
5965         }
5966         
5967         if (this.cls) {
5968             cfg.cls+=  ' ' +this.cls;
5969         }
5970         
5971         // this lot should be simplifed...
5972         
5973         if (this.align) {
5974             cfg.align=this.align;
5975         }
5976         if (this.bgcolor) {
5977             cfg.bgcolor=this.bgcolor;
5978         }
5979         if (this.border) {
5980             cfg.border=this.border;
5981         }
5982         if (this.cellpadding) {
5983             cfg.cellpadding=this.cellpadding;
5984         }
5985         if (this.cellspacing) {
5986             cfg.cellspacing=this.cellspacing;
5987         }
5988         if (this.frame) {
5989             cfg.frame=this.frame;
5990         }
5991         if (this.rules) {
5992             cfg.rules=this.rules;
5993         }
5994         if (this.sortable) {
5995             cfg.sortable=this.sortable;
5996         }
5997         if (this.summary) {
5998             cfg.summary=this.summary;
5999         }
6000         if (this.width) {
6001             cfg.width=this.width;
6002         }
6003         if (this.layout) {
6004             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6005         }
6006         
6007         if(this.store || this.cm){
6008             if(this.headerShow){
6009                 cfg.cn.push(this.renderHeader());
6010             }
6011             
6012             cfg.cn.push(this.renderBody());
6013             
6014             if(this.footerShow){
6015                 cfg.cn.push(this.renderFooter());
6016             }
6017             // where does this come from?
6018             //cfg.cls+=  ' TableGrid';
6019         }
6020         
6021         return { cn : [ cfg ] };
6022     },
6023     
6024     initEvents : function()
6025     {   
6026         if(!this.store || !this.cm){
6027             return;
6028         }
6029         if (this.selModel) {
6030             this.selModel.initEvents();
6031         }
6032         
6033         
6034         //Roo.log('initEvents with ds!!!!');
6035         
6036         this.mainBody = this.el.select('tbody', true).first();
6037         this.mainHead = this.el.select('thead', true).first();
6038         
6039         
6040         
6041         
6042         var _this = this;
6043         
6044         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6045             e.on('click', _this.sort, _this);
6046         });
6047         
6048         this.mainBody.on("click", this.onClick, this);
6049         this.mainBody.on("dblclick", this.onDblClick, this);
6050         
6051         // why is this done????? = it breaks dialogs??
6052         //this.parent().el.setStyle('position', 'relative');
6053         
6054         
6055         if (this.footer) {
6056             this.footer.parentId = this.id;
6057             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6058         } 
6059         
6060         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6061         
6062         this.store.on('load', this.onLoad, this);
6063         this.store.on('beforeload', this.onBeforeLoad, this);
6064         this.store.on('update', this.onUpdate, this);
6065         this.store.on('add', this.onAdd, this);
6066         this.store.on("clear", this.clear, this);
6067         
6068         this.el.on("contextmenu", this.onContextMenu, this);
6069         
6070         this.mainBody.on('scroll', this.onBodyScroll, this);
6071         
6072         
6073     },
6074     
6075     onContextMenu : function(e, t)
6076     {
6077         this.processEvent("contextmenu", e);
6078     },
6079     
6080     processEvent : function(name, e)
6081     {
6082         if (name != 'touchstart' ) {
6083             this.fireEvent(name, e);    
6084         }
6085         
6086         var t = e.getTarget();
6087         
6088         var cell = Roo.get(t);
6089         
6090         if(!cell){
6091             return;
6092         }
6093         
6094         if(cell.findParent('tfoot', false, true)){
6095             return;
6096         }
6097         
6098         if(cell.findParent('thead', false, true)){
6099             
6100             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6101                 cell = Roo.get(t).findParent('th', false, true);
6102                 if (!cell) {
6103                     Roo.log("failed to find th in thead?");
6104                     Roo.log(e.getTarget());
6105                     return;
6106                 }
6107             }
6108             
6109             var cellIndex = cell.dom.cellIndex;
6110             
6111             var ename = name == 'touchstart' ? 'click' : name;
6112             this.fireEvent("header" + ename, this, cellIndex, e);
6113             
6114             return;
6115         }
6116         
6117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6118             cell = Roo.get(t).findParent('td', false, true);
6119             if (!cell) {
6120                 Roo.log("failed to find th in tbody?");
6121                 Roo.log(e.getTarget());
6122                 return;
6123             }
6124         }
6125         
6126         var row = cell.findParent('tr', false, true);
6127         var cellIndex = cell.dom.cellIndex;
6128         var rowIndex = row.dom.rowIndex - 1;
6129         
6130         if(row !== false){
6131             
6132             this.fireEvent("row" + name, this, rowIndex, e);
6133             
6134             if(cell !== false){
6135             
6136                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6137             }
6138         }
6139         
6140     },
6141     
6142     onMouseover : function(e, el)
6143     {
6144         var cell = Roo.get(el);
6145         
6146         if(!cell){
6147             return;
6148         }
6149         
6150         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151             cell = cell.findParent('td', false, true);
6152         }
6153         
6154         var row = cell.findParent('tr', false, true);
6155         var cellIndex = cell.dom.cellIndex;
6156         var rowIndex = row.dom.rowIndex - 1; // start from 0
6157         
6158         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6159         
6160     },
6161     
6162     onMouseout : function(e, el)
6163     {
6164         var cell = Roo.get(el);
6165         
6166         if(!cell){
6167             return;
6168         }
6169         
6170         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6171             cell = cell.findParent('td', false, true);
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1; // start from 0
6177         
6178         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6179         
6180     },
6181     
6182     onClick : function(e, el)
6183     {
6184         var cell = Roo.get(el);
6185         
6186         if(!cell || (!this.cellSelection && !this.rowSelection)){
6187             return;
6188         }
6189         
6190         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6191             cell = cell.findParent('td', false, true);
6192         }
6193         
6194         if(!cell || typeof(cell) == 'undefined'){
6195             return;
6196         }
6197         
6198         var row = cell.findParent('tr', false, true);
6199         
6200         if(!row || typeof(row) == 'undefined'){
6201             return;
6202         }
6203         
6204         var cellIndex = cell.dom.cellIndex;
6205         var rowIndex = this.getRowIndex(row);
6206         
6207         // why??? - should these not be based on SelectionModel?
6208         if(this.cellSelection){
6209             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6210         }
6211         
6212         if(this.rowSelection){
6213             this.fireEvent('rowclick', this, row, rowIndex, e);
6214         }
6215         
6216         
6217     },
6218         
6219     onDblClick : function(e,el)
6220     {
6221         var cell = Roo.get(el);
6222         
6223         if(!cell || (!this.cellSelection && !this.rowSelection)){
6224             return;
6225         }
6226         
6227         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6228             cell = cell.findParent('td', false, true);
6229         }
6230         
6231         if(!cell || typeof(cell) == 'undefined'){
6232             return;
6233         }
6234         
6235         var row = cell.findParent('tr', false, true);
6236         
6237         if(!row || typeof(row) == 'undefined'){
6238             return;
6239         }
6240         
6241         var cellIndex = cell.dom.cellIndex;
6242         var rowIndex = this.getRowIndex(row);
6243         
6244         if(this.cellSelection){
6245             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6246         }
6247         
6248         if(this.rowSelection){
6249             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6250         }
6251     },
6252     
6253     sort : function(e,el)
6254     {
6255         var col = Roo.get(el);
6256         
6257         if(!col.hasClass('sortable')){
6258             return;
6259         }
6260         
6261         var sort = col.attr('sort');
6262         var dir = 'ASC';
6263         
6264         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6265             dir = 'DESC';
6266         }
6267         
6268         this.store.sortInfo = {field : sort, direction : dir};
6269         
6270         if (this.footer) {
6271             Roo.log("calling footer first");
6272             this.footer.onClick('first');
6273         } else {
6274         
6275             this.store.load({ params : { start : 0 } });
6276         }
6277     },
6278     
6279     renderHeader : function()
6280     {
6281         var header = {
6282             tag: 'thead',
6283             cn : []
6284         };
6285         
6286         var cm = this.cm;
6287         this.totalWidth = 0;
6288         
6289         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6290             
6291             var config = cm.config[i];
6292             
6293             var c = {
6294                 tag: 'th',
6295                 style : '',
6296                 html: cm.getColumnHeader(i)
6297             };
6298             
6299             var hh = '';
6300             
6301             if(typeof(config.sortable) != 'undefined' && config.sortable){
6302                 c.cls = 'sortable';
6303                 c.html = '<i class="glyphicon"></i>' + c.html;
6304             }
6305             
6306             if(typeof(config.lgHeader) != 'undefined'){
6307                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6308             }
6309             
6310             if(typeof(config.mdHeader) != 'undefined'){
6311                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6312             }
6313             
6314             if(typeof(config.smHeader) != 'undefined'){
6315                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6316             }
6317             
6318             if(typeof(config.xsHeader) != 'undefined'){
6319                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6320             }
6321             
6322             if(hh.length){
6323                 c.html = hh;
6324             }
6325             
6326             if(typeof(config.tooltip) != 'undefined'){
6327                 c.tooltip = config.tooltip;
6328             }
6329             
6330             if(typeof(config.colspan) != 'undefined'){
6331                 c.colspan = config.colspan;
6332             }
6333             
6334             if(typeof(config.hidden) != 'undefined' && config.hidden){
6335                 c.style += ' display:none;';
6336             }
6337             
6338             if(typeof(config.dataIndex) != 'undefined'){
6339                 c.sort = config.dataIndex;
6340             }
6341             
6342            
6343             
6344             if(typeof(config.align) != 'undefined' && config.align.length){
6345                 c.style += ' text-align:' + config.align + ';';
6346             }
6347             
6348             if(typeof(config.width) != 'undefined'){
6349                 c.style += ' width:' + config.width + 'px;';
6350                 this.totalWidth += config.width;
6351             } else {
6352                 this.totalWidth += 100; // assume minimum of 100 per column?
6353             }
6354             
6355             if(typeof(config.cls) != 'undefined'){
6356                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6357             }
6358             
6359             ['xs','sm','md','lg'].map(function(size){
6360                 
6361                 if(typeof(config[size]) == 'undefined'){
6362                     return;
6363                 }
6364                 
6365                 if (!config[size]) { // 0 = hidden
6366                     c.cls += ' hidden-' + size;
6367                     return;
6368                 }
6369                 
6370                 c.cls += ' col-' + size + '-' + config[size];
6371
6372             });
6373             
6374             header.cn.push(c)
6375         }
6376         
6377         return header;
6378     },
6379     
6380     renderBody : function()
6381     {
6382         var body = {
6383             tag: 'tbody',
6384             cn : [
6385                 {
6386                     tag: 'tr',
6387                     cn : [
6388                         {
6389                             tag : 'td',
6390                             colspan :  this.cm.getColumnCount()
6391                         }
6392                     ]
6393                 }
6394             ]
6395         };
6396         
6397         return body;
6398     },
6399     
6400     renderFooter : function()
6401     {
6402         var footer = {
6403             tag: 'tfoot',
6404             cn : [
6405                 {
6406                     tag: 'tr',
6407                     cn : [
6408                         {
6409                             tag : 'td',
6410                             colspan :  this.cm.getColumnCount()
6411                         }
6412                     ]
6413                 }
6414             ]
6415         };
6416         
6417         return footer;
6418     },
6419     
6420     
6421     
6422     onLoad : function()
6423     {
6424 //        Roo.log('ds onload');
6425         this.clear();
6426         
6427         var _this = this;
6428         var cm = this.cm;
6429         var ds = this.store;
6430         
6431         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6432             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6433             if (_this.store.sortInfo) {
6434                     
6435                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6436                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6437                 }
6438                 
6439                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6440                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6441                 }
6442             }
6443         });
6444         
6445         var tbody =  this.mainBody;
6446               
6447         if(ds.getCount() > 0){
6448             ds.data.each(function(d,rowIndex){
6449                 var row =  this.renderRow(cm, ds, rowIndex);
6450                 
6451                 tbody.createChild(row);
6452                 
6453                 var _this = this;
6454                 
6455                 if(row.cellObjects.length){
6456                     Roo.each(row.cellObjects, function(r){
6457                         _this.renderCellObject(r);
6458                     })
6459                 }
6460                 
6461             }, this);
6462         }
6463         
6464         Roo.each(this.el.select('tbody td', true).elements, function(e){
6465             e.on('mouseover', _this.onMouseover, _this);
6466         });
6467         
6468         Roo.each(this.el.select('tbody td', true).elements, function(e){
6469             e.on('mouseout', _this.onMouseout, _this);
6470         });
6471         this.fireEvent('rowsrendered', this);
6472         //if(this.loadMask){
6473         //    this.maskEl.hide();
6474         //}
6475         
6476         this.autoSize();
6477     },
6478     
6479     
6480     onUpdate : function(ds,record)
6481     {
6482         this.refreshRow(record);
6483         this.autoSize();
6484     },
6485     
6486     onRemove : function(ds, record, index, isUpdate){
6487         if(isUpdate !== true){
6488             this.fireEvent("beforerowremoved", this, index, record);
6489         }
6490         var bt = this.mainBody.dom;
6491         
6492         var rows = this.el.select('tbody > tr', true).elements;
6493         
6494         if(typeof(rows[index]) != 'undefined'){
6495             bt.removeChild(rows[index].dom);
6496         }
6497         
6498 //        if(bt.rows[index]){
6499 //            bt.removeChild(bt.rows[index]);
6500 //        }
6501         
6502         if(isUpdate !== true){
6503             //this.stripeRows(index);
6504             //this.syncRowHeights(index, index);
6505             //this.layout();
6506             this.fireEvent("rowremoved", this, index, record);
6507         }
6508     },
6509     
6510     onAdd : function(ds, records, rowIndex)
6511     {
6512         //Roo.log('on Add called');
6513         // - note this does not handle multiple adding very well..
6514         var bt = this.mainBody.dom;
6515         for (var i =0 ; i < records.length;i++) {
6516             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6517             //Roo.log(records[i]);
6518             //Roo.log(this.store.getAt(rowIndex+i));
6519             this.insertRow(this.store, rowIndex + i, false);
6520             return;
6521         }
6522         
6523     },
6524     
6525     
6526     refreshRow : function(record){
6527         var ds = this.store, index;
6528         if(typeof record == 'number'){
6529             index = record;
6530             record = ds.getAt(index);
6531         }else{
6532             index = ds.indexOf(record);
6533         }
6534         this.insertRow(ds, index, true);
6535         this.autoSize();
6536         this.onRemove(ds, record, index+1, true);
6537         this.autoSize();
6538         //this.syncRowHeights(index, index);
6539         //this.layout();
6540         this.fireEvent("rowupdated", this, index, record);
6541     },
6542     
6543     insertRow : function(dm, rowIndex, isUpdate){
6544         
6545         if(!isUpdate){
6546             this.fireEvent("beforerowsinserted", this, rowIndex);
6547         }
6548             //var s = this.getScrollState();
6549         var row = this.renderRow(this.cm, this.store, rowIndex);
6550         // insert before rowIndex..
6551         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6552         
6553         var _this = this;
6554                 
6555         if(row.cellObjects.length){
6556             Roo.each(row.cellObjects, function(r){
6557                 _this.renderCellObject(r);
6558             })
6559         }
6560             
6561         if(!isUpdate){
6562             this.fireEvent("rowsinserted", this, rowIndex);
6563             //this.syncRowHeights(firstRow, lastRow);
6564             //this.stripeRows(firstRow);
6565             //this.layout();
6566         }
6567         
6568     },
6569     
6570     
6571     getRowDom : function(rowIndex)
6572     {
6573         var rows = this.el.select('tbody > tr', true).elements;
6574         
6575         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6576         
6577     },
6578     // returns the object tree for a tr..
6579   
6580     
6581     renderRow : function(cm, ds, rowIndex) 
6582     {
6583         
6584         var d = ds.getAt(rowIndex);
6585         
6586         var row = {
6587             tag : 'tr',
6588             cn : []
6589         };
6590             
6591         var cellObjects = [];
6592         
6593         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6594             var config = cm.config[i];
6595             
6596             var renderer = cm.getRenderer(i);
6597             var value = '';
6598             var id = false;
6599             
6600             if(typeof(renderer) !== 'undefined'){
6601                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6602             }
6603             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6604             // and are rendered into the cells after the row is rendered - using the id for the element.
6605             
6606             if(typeof(value) === 'object'){
6607                 id = Roo.id();
6608                 cellObjects.push({
6609                     container : id,
6610                     cfg : value 
6611                 })
6612             }
6613             
6614             var rowcfg = {
6615                 record: d,
6616                 rowIndex : rowIndex,
6617                 colIndex : i,
6618                 rowClass : ''
6619             };
6620
6621             this.fireEvent('rowclass', this, rowcfg);
6622             
6623             var td = {
6624                 tag: 'td',
6625                 cls : rowcfg.rowClass,
6626                 style: '',
6627                 html: (typeof(value) === 'object') ? '' : value
6628             };
6629             
6630             if (id) {
6631                 td.id = id;
6632             }
6633             
6634             if(typeof(config.colspan) != 'undefined'){
6635                 td.colspan = config.colspan;
6636             }
6637             
6638             if(typeof(config.hidden) != 'undefined' && config.hidden){
6639                 td.style += ' display:none;';
6640             }
6641             
6642             if(typeof(config.align) != 'undefined' && config.align.length){
6643                 td.style += ' text-align:' + config.align + ';';
6644             }
6645             
6646             if(typeof(config.width) != 'undefined'){
6647                 td.style += ' width:' +  config.width + 'px;';
6648             }
6649             
6650             if(typeof(config.cursor) != 'undefined'){
6651                 td.style += ' cursor:' +  config.cursor + ';';
6652             }
6653             
6654             if(typeof(config.cls) != 'undefined'){
6655                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6656             }
6657             
6658             ['xs','sm','md','lg'].map(function(size){
6659                 
6660                 if(typeof(config[size]) == 'undefined'){
6661                     return;
6662                 }
6663                 
6664                 if (!config[size]) { // 0 = hidden
6665                     td.cls += ' hidden-' + size;
6666                     return;
6667                 }
6668                 
6669                 td.cls += ' col-' + size + '-' + config[size];
6670
6671             });
6672              
6673             row.cn.push(td);
6674            
6675         }
6676         
6677         row.cellObjects = cellObjects;
6678         
6679         return row;
6680           
6681     },
6682     
6683     
6684     
6685     onBeforeLoad : function()
6686     {
6687         //Roo.log('ds onBeforeLoad');
6688         
6689         //this.clear();
6690         
6691         //if(this.loadMask){
6692         //    this.maskEl.show();
6693         //}
6694     },
6695      /**
6696      * Remove all rows
6697      */
6698     clear : function()
6699     {
6700         this.el.select('tbody', true).first().dom.innerHTML = '';
6701     },
6702     /**
6703      * Show or hide a row.
6704      * @param {Number} rowIndex to show or hide
6705      * @param {Boolean} state hide
6706      */
6707     setRowVisibility : function(rowIndex, state)
6708     {
6709         var bt = this.mainBody.dom;
6710         
6711         var rows = this.el.select('tbody > tr', true).elements;
6712         
6713         if(typeof(rows[rowIndex]) == 'undefined'){
6714             return;
6715         }
6716         rows[rowIndex].dom.style.display = state ? '' : 'none';
6717     },
6718     
6719     
6720     getSelectionModel : function(){
6721         if(!this.selModel){
6722             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6723         }
6724         return this.selModel;
6725     },
6726     /*
6727      * Render the Roo.bootstrap object from renderder
6728      */
6729     renderCellObject : function(r)
6730     {
6731         var _this = this;
6732         
6733         var t = r.cfg.render(r.container);
6734         
6735         if(r.cfg.cn){
6736             Roo.each(r.cfg.cn, function(c){
6737                 var child = {
6738                     container: t.getChildContainer(),
6739                     cfg: c
6740                 };
6741                 _this.renderCellObject(child);
6742             })
6743         }
6744     },
6745     
6746     getRowIndex : function(row)
6747     {
6748         var rowIndex = -1;
6749         
6750         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6751             if(el != row){
6752                 return;
6753             }
6754             
6755             rowIndex = index;
6756         });
6757         
6758         return rowIndex;
6759     },
6760      /**
6761      * Returns the grid's underlying element = used by panel.Grid
6762      * @return {Element} The element
6763      */
6764     getGridEl : function(){
6765         return this.el;
6766     },
6767      /**
6768      * Forces a resize - used by panel.Grid
6769      * @return {Element} The element
6770      */
6771     autoSize : function()
6772     {
6773         //var ctr = Roo.get(this.container.dom.parentElement);
6774         var ctr = Roo.get(this.el.dom);
6775         
6776         var thd = this.getGridEl().select('thead',true).first();
6777         var tbd = this.getGridEl().select('tbody', true).first();
6778         var tfd = this.getGridEl().select('tfoot', true).first();
6779         
6780         var cw = ctr.getWidth();
6781         
6782         if (tbd) {
6783             
6784             tbd.setSize(ctr.getWidth(),
6785                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6786             );
6787             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6788             cw -= barsize;
6789         }
6790         cw = Math.max(cw, this.totalWidth);
6791         this.getGridEl().select('tr',true).setWidth(cw);
6792         // resize 'expandable coloumn?
6793         
6794         return; // we doe not have a view in this design..
6795         
6796     },
6797     onBodyScroll: function()
6798     {
6799         
6800         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6801         this.mainHead.setStyle({
6802                     'position' : 'relative',
6803                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6804         });
6805         
6806         
6807     }
6808 });
6809
6810  
6811
6812  /*
6813  * - LGPL
6814  *
6815  * table cell
6816  * 
6817  */
6818
6819 /**
6820  * @class Roo.bootstrap.TableCell
6821  * @extends Roo.bootstrap.Component
6822  * Bootstrap TableCell class
6823  * @cfg {String} html cell contain text
6824  * @cfg {String} cls cell class
6825  * @cfg {String} tag cell tag (td|th) default td
6826  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6827  * @cfg {String} align Aligns the content in a cell
6828  * @cfg {String} axis Categorizes cells
6829  * @cfg {String} bgcolor Specifies the background color of a cell
6830  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6831  * @cfg {Number} colspan Specifies the number of columns a cell should span
6832  * @cfg {String} headers Specifies one or more header cells a cell is related to
6833  * @cfg {Number} height Sets the height of a cell
6834  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6835  * @cfg {Number} rowspan Sets the number of rows a cell should span
6836  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6837  * @cfg {String} valign Vertical aligns the content in a cell
6838  * @cfg {Number} width Specifies the width of a cell
6839  * 
6840  * @constructor
6841  * Create a new TableCell
6842  * @param {Object} config The config object
6843  */
6844
6845 Roo.bootstrap.TableCell = function(config){
6846     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6847 };
6848
6849 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6850     
6851     html: false,
6852     cls: false,
6853     tag: false,
6854     abbr: false,
6855     align: false,
6856     axis: false,
6857     bgcolor: false,
6858     charoff: false,
6859     colspan: false,
6860     headers: false,
6861     height: false,
6862     nowrap: false,
6863     rowspan: false,
6864     scope: false,
6865     valign: false,
6866     width: false,
6867     
6868     
6869     getAutoCreate : function(){
6870         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6871         
6872         cfg = {
6873             tag: 'td'
6874         };
6875         
6876         if(this.tag){
6877             cfg.tag = this.tag;
6878         }
6879         
6880         if (this.html) {
6881             cfg.html=this.html
6882         }
6883         if (this.cls) {
6884             cfg.cls=this.cls
6885         }
6886         if (this.abbr) {
6887             cfg.abbr=this.abbr
6888         }
6889         if (this.align) {
6890             cfg.align=this.align
6891         }
6892         if (this.axis) {
6893             cfg.axis=this.axis
6894         }
6895         if (this.bgcolor) {
6896             cfg.bgcolor=this.bgcolor
6897         }
6898         if (this.charoff) {
6899             cfg.charoff=this.charoff
6900         }
6901         if (this.colspan) {
6902             cfg.colspan=this.colspan
6903         }
6904         if (this.headers) {
6905             cfg.headers=this.headers
6906         }
6907         if (this.height) {
6908             cfg.height=this.height
6909         }
6910         if (this.nowrap) {
6911             cfg.nowrap=this.nowrap
6912         }
6913         if (this.rowspan) {
6914             cfg.rowspan=this.rowspan
6915         }
6916         if (this.scope) {
6917             cfg.scope=this.scope
6918         }
6919         if (this.valign) {
6920             cfg.valign=this.valign
6921         }
6922         if (this.width) {
6923             cfg.width=this.width
6924         }
6925         
6926         
6927         return cfg;
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * table row
6938  * 
6939  */
6940
6941 /**
6942  * @class Roo.bootstrap.TableRow
6943  * @extends Roo.bootstrap.Component
6944  * Bootstrap TableRow class
6945  * @cfg {String} cls row class
6946  * @cfg {String} align Aligns the content in a table row
6947  * @cfg {String} bgcolor Specifies a background color for a table row
6948  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6949  * @cfg {String} valign Vertical aligns the content in a table row
6950  * 
6951  * @constructor
6952  * Create a new TableRow
6953  * @param {Object} config The config object
6954  */
6955
6956 Roo.bootstrap.TableRow = function(config){
6957     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6958 };
6959
6960 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6961     
6962     cls: false,
6963     align: false,
6964     bgcolor: false,
6965     charoff: false,
6966     valign: false,
6967     
6968     getAutoCreate : function(){
6969         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6970         
6971         cfg = {
6972             tag: 'tr'
6973         };
6974             
6975         if(this.cls){
6976             cfg.cls = this.cls;
6977         }
6978         if(this.align){
6979             cfg.align = this.align;
6980         }
6981         if(this.bgcolor){
6982             cfg.bgcolor = this.bgcolor;
6983         }
6984         if(this.charoff){
6985             cfg.charoff = this.charoff;
6986         }
6987         if(this.valign){
6988             cfg.valign = this.valign;
6989         }
6990         
6991         return cfg;
6992     }
6993    
6994 });
6995
6996  
6997
6998  /*
6999  * - LGPL
7000  *
7001  * table body
7002  * 
7003  */
7004
7005 /**
7006  * @class Roo.bootstrap.TableBody
7007  * @extends Roo.bootstrap.Component
7008  * Bootstrap TableBody class
7009  * @cfg {String} cls element class
7010  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7011  * @cfg {String} align Aligns the content inside the element
7012  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7013  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7014  * 
7015  * @constructor
7016  * Create a new TableBody
7017  * @param {Object} config The config object
7018  */
7019
7020 Roo.bootstrap.TableBody = function(config){
7021     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7022 };
7023
7024 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7025     
7026     cls: false,
7027     tag: false,
7028     align: false,
7029     charoff: false,
7030     valign: false,
7031     
7032     getAutoCreate : function(){
7033         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7034         
7035         cfg = {
7036             tag: 'tbody'
7037         };
7038             
7039         if (this.cls) {
7040             cfg.cls=this.cls
7041         }
7042         if(this.tag){
7043             cfg.tag = this.tag;
7044         }
7045         
7046         if(this.align){
7047             cfg.align = this.align;
7048         }
7049         if(this.charoff){
7050             cfg.charoff = this.charoff;
7051         }
7052         if(this.valign){
7053             cfg.valign = this.valign;
7054         }
7055         
7056         return cfg;
7057     }
7058     
7059     
7060 //    initEvents : function()
7061 //    {
7062 //        
7063 //        if(!this.store){
7064 //            return;
7065 //        }
7066 //        
7067 //        this.store = Roo.factory(this.store, Roo.data);
7068 //        this.store.on('load', this.onLoad, this);
7069 //        
7070 //        this.store.load();
7071 //        
7072 //    },
7073 //    
7074 //    onLoad: function () 
7075 //    {   
7076 //        this.fireEvent('load', this);
7077 //    }
7078 //    
7079 //   
7080 });
7081
7082  
7083
7084  /*
7085  * Based on:
7086  * Ext JS Library 1.1.1
7087  * Copyright(c) 2006-2007, Ext JS, LLC.
7088  *
7089  * Originally Released Under LGPL - original licence link has changed is not relivant.
7090  *
7091  * Fork - LGPL
7092  * <script type="text/javascript">
7093  */
7094
7095 // as we use this in bootstrap.
7096 Roo.namespace('Roo.form');
7097  /**
7098  * @class Roo.form.Action
7099  * Internal Class used to handle form actions
7100  * @constructor
7101  * @param {Roo.form.BasicForm} el The form element or its id
7102  * @param {Object} config Configuration options
7103  */
7104
7105  
7106  
7107 // define the action interface
7108 Roo.form.Action = function(form, options){
7109     this.form = form;
7110     this.options = options || {};
7111 };
7112 /**
7113  * Client Validation Failed
7114  * @const 
7115  */
7116 Roo.form.Action.CLIENT_INVALID = 'client';
7117 /**
7118  * Server Validation Failed
7119  * @const 
7120  */
7121 Roo.form.Action.SERVER_INVALID = 'server';
7122  /**
7123  * Connect to Server Failed
7124  * @const 
7125  */
7126 Roo.form.Action.CONNECT_FAILURE = 'connect';
7127 /**
7128  * Reading Data from Server Failed
7129  * @const 
7130  */
7131 Roo.form.Action.LOAD_FAILURE = 'load';
7132
7133 Roo.form.Action.prototype = {
7134     type : 'default',
7135     failureType : undefined,
7136     response : undefined,
7137     result : undefined,
7138
7139     // interface method
7140     run : function(options){
7141
7142     },
7143
7144     // interface method
7145     success : function(response){
7146
7147     },
7148
7149     // interface method
7150     handleResponse : function(response){
7151
7152     },
7153
7154     // default connection failure
7155     failure : function(response){
7156         
7157         this.response = response;
7158         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7159         this.form.afterAction(this, false);
7160     },
7161
7162     processResponse : function(response){
7163         this.response = response;
7164         if(!response.responseText){
7165             return true;
7166         }
7167         this.result = this.handleResponse(response);
7168         return this.result;
7169     },
7170
7171     // utility functions used internally
7172     getUrl : function(appendParams){
7173         var url = this.options.url || this.form.url || this.form.el.dom.action;
7174         if(appendParams){
7175             var p = this.getParams();
7176             if(p){
7177                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7178             }
7179         }
7180         return url;
7181     },
7182
7183     getMethod : function(){
7184         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7185     },
7186
7187     getParams : function(){
7188         var bp = this.form.baseParams;
7189         var p = this.options.params;
7190         if(p){
7191             if(typeof p == "object"){
7192                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7193             }else if(typeof p == 'string' && bp){
7194                 p += '&' + Roo.urlEncode(bp);
7195             }
7196         }else if(bp){
7197             p = Roo.urlEncode(bp);
7198         }
7199         return p;
7200     },
7201
7202     createCallback : function(){
7203         return {
7204             success: this.success,
7205             failure: this.failure,
7206             scope: this,
7207             timeout: (this.form.timeout*1000),
7208             upload: this.form.fileUpload ? this.success : undefined
7209         };
7210     }
7211 };
7212
7213 Roo.form.Action.Submit = function(form, options){
7214     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7215 };
7216
7217 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7218     type : 'submit',
7219
7220     haveProgress : false,
7221     uploadComplete : false,
7222     
7223     // uploadProgress indicator.
7224     uploadProgress : function()
7225     {
7226         if (!this.form.progressUrl) {
7227             return;
7228         }
7229         
7230         if (!this.haveProgress) {
7231             Roo.MessageBox.progress("Uploading", "Uploading");
7232         }
7233         if (this.uploadComplete) {
7234            Roo.MessageBox.hide();
7235            return;
7236         }
7237         
7238         this.haveProgress = true;
7239    
7240         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7241         
7242         var c = new Roo.data.Connection();
7243         c.request({
7244             url : this.form.progressUrl,
7245             params: {
7246                 id : uid
7247             },
7248             method: 'GET',
7249             success : function(req){
7250                //console.log(data);
7251                 var rdata = false;
7252                 var edata;
7253                 try  {
7254                    rdata = Roo.decode(req.responseText)
7255                 } catch (e) {
7256                     Roo.log("Invalid data from server..");
7257                     Roo.log(edata);
7258                     return;
7259                 }
7260                 if (!rdata || !rdata.success) {
7261                     Roo.log(rdata);
7262                     Roo.MessageBox.alert(Roo.encode(rdata));
7263                     return;
7264                 }
7265                 var data = rdata.data;
7266                 
7267                 if (this.uploadComplete) {
7268                    Roo.MessageBox.hide();
7269                    return;
7270                 }
7271                    
7272                 if (data){
7273                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7274                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7275                     );
7276                 }
7277                 this.uploadProgress.defer(2000,this);
7278             },
7279        
7280             failure: function(data) {
7281                 Roo.log('progress url failed ');
7282                 Roo.log(data);
7283             },
7284             scope : this
7285         });
7286            
7287     },
7288     
7289     
7290     run : function()
7291     {
7292         // run get Values on the form, so it syncs any secondary forms.
7293         this.form.getValues();
7294         
7295         var o = this.options;
7296         var method = this.getMethod();
7297         var isPost = method == 'POST';
7298         if(o.clientValidation === false || this.form.isValid()){
7299             
7300             if (this.form.progressUrl) {
7301                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7302                     (new Date() * 1) + '' + Math.random());
7303                     
7304             } 
7305             
7306             
7307             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7308                 form:this.form.el.dom,
7309                 url:this.getUrl(!isPost),
7310                 method: method,
7311                 params:isPost ? this.getParams() : null,
7312                 isUpload: this.form.fileUpload
7313             }));
7314             
7315             this.uploadProgress();
7316
7317         }else if (o.clientValidation !== false){ // client validation failed
7318             this.failureType = Roo.form.Action.CLIENT_INVALID;
7319             this.form.afterAction(this, false);
7320         }
7321     },
7322
7323     success : function(response)
7324     {
7325         this.uploadComplete= true;
7326         if (this.haveProgress) {
7327             Roo.MessageBox.hide();
7328         }
7329         
7330         
7331         var result = this.processResponse(response);
7332         if(result === true || result.success){
7333             this.form.afterAction(this, true);
7334             return;
7335         }
7336         if(result.errors){
7337             this.form.markInvalid(result.errors);
7338             this.failureType = Roo.form.Action.SERVER_INVALID;
7339         }
7340         this.form.afterAction(this, false);
7341     },
7342     failure : function(response)
7343     {
7344         this.uploadComplete= true;
7345         if (this.haveProgress) {
7346             Roo.MessageBox.hide();
7347         }
7348         
7349         this.response = response;
7350         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7351         this.form.afterAction(this, false);
7352     },
7353     
7354     handleResponse : function(response){
7355         if(this.form.errorReader){
7356             var rs = this.form.errorReader.read(response);
7357             var errors = [];
7358             if(rs.records){
7359                 for(var i = 0, len = rs.records.length; i < len; i++) {
7360                     var r = rs.records[i];
7361                     errors[i] = r.data;
7362                 }
7363             }
7364             if(errors.length < 1){
7365                 errors = null;
7366             }
7367             return {
7368                 success : rs.success,
7369                 errors : errors
7370             };
7371         }
7372         var ret = false;
7373         try {
7374             ret = Roo.decode(response.responseText);
7375         } catch (e) {
7376             ret = {
7377                 success: false,
7378                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7379                 errors : []
7380             };
7381         }
7382         return ret;
7383         
7384     }
7385 });
7386
7387
7388 Roo.form.Action.Load = function(form, options){
7389     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7390     this.reader = this.form.reader;
7391 };
7392
7393 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7394     type : 'load',
7395
7396     run : function(){
7397         
7398         Roo.Ajax.request(Roo.apply(
7399                 this.createCallback(), {
7400                     method:this.getMethod(),
7401                     url:this.getUrl(false),
7402                     params:this.getParams()
7403         }));
7404     },
7405
7406     success : function(response){
7407         
7408         var result = this.processResponse(response);
7409         if(result === true || !result.success || !result.data){
7410             this.failureType = Roo.form.Action.LOAD_FAILURE;
7411             this.form.afterAction(this, false);
7412             return;
7413         }
7414         this.form.clearInvalid();
7415         this.form.setValues(result.data);
7416         this.form.afterAction(this, true);
7417     },
7418
7419     handleResponse : function(response){
7420         if(this.form.reader){
7421             var rs = this.form.reader.read(response);
7422             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7423             return {
7424                 success : rs.success,
7425                 data : data
7426             };
7427         }
7428         return Roo.decode(response.responseText);
7429     }
7430 });
7431
7432 Roo.form.Action.ACTION_TYPES = {
7433     'load' : Roo.form.Action.Load,
7434     'submit' : Roo.form.Action.Submit
7435 };/*
7436  * - LGPL
7437  *
7438  * form
7439  *
7440  */
7441
7442 /**
7443  * @class Roo.bootstrap.Form
7444  * @extends Roo.bootstrap.Component
7445  * Bootstrap Form class
7446  * @cfg {String} method  GET | POST (default POST)
7447  * @cfg {String} labelAlign top | left (default top)
7448  * @cfg {String} align left  | right - for navbars
7449  * @cfg {Boolean} loadMask load mask when submit (default true)
7450
7451  *
7452  * @constructor
7453  * Create a new Form
7454  * @param {Object} config The config object
7455  */
7456
7457
7458 Roo.bootstrap.Form = function(config){
7459     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7460     
7461     Roo.bootstrap.Form.popover.apply();
7462     
7463     this.addEvents({
7464         /**
7465          * @event clientvalidation
7466          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7467          * @param {Form} this
7468          * @param {Boolean} valid true if the form has passed client-side validation
7469          */
7470         clientvalidation: true,
7471         /**
7472          * @event beforeaction
7473          * Fires before any action is performed. Return false to cancel the action.
7474          * @param {Form} this
7475          * @param {Action} action The action to be performed
7476          */
7477         beforeaction: true,
7478         /**
7479          * @event actionfailed
7480          * Fires when an action fails.
7481          * @param {Form} this
7482          * @param {Action} action The action that failed
7483          */
7484         actionfailed : true,
7485         /**
7486          * @event actioncomplete
7487          * Fires when an action is completed.
7488          * @param {Form} this
7489          * @param {Action} action The action that completed
7490          */
7491         actioncomplete : true
7492     });
7493
7494 };
7495
7496 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7497
7498      /**
7499      * @cfg {String} method
7500      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7501      */
7502     method : 'POST',
7503     /**
7504      * @cfg {String} url
7505      * The URL to use for form actions if one isn't supplied in the action options.
7506      */
7507     /**
7508      * @cfg {Boolean} fileUpload
7509      * Set to true if this form is a file upload.
7510      */
7511
7512     /**
7513      * @cfg {Object} baseParams
7514      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7515      */
7516
7517     /**
7518      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7519      */
7520     timeout: 30,
7521     /**
7522      * @cfg {Sting} align (left|right) for navbar forms
7523      */
7524     align : 'left',
7525
7526     // private
7527     activeAction : null,
7528
7529     /**
7530      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7531      * element by passing it or its id or mask the form itself by passing in true.
7532      * @type Mixed
7533      */
7534     waitMsgTarget : false,
7535
7536     loadMask : true,
7537     
7538     /**
7539      * @cfg {Boolean} errorMask (true|false) default false
7540      */
7541     errorMask : false,
7542
7543     getAutoCreate : function(){
7544
7545         var cfg = {
7546             tag: 'form',
7547             method : this.method || 'POST',
7548             id : this.id || Roo.id(),
7549             cls : ''
7550         };
7551         if (this.parent().xtype.match(/^Nav/)) {
7552             cfg.cls = 'navbar-form navbar-' + this.align;
7553
7554         }
7555
7556         if (this.labelAlign == 'left' ) {
7557             cfg.cls += ' form-horizontal';
7558         }
7559
7560
7561         return cfg;
7562     },
7563     initEvents : function()
7564     {
7565         this.el.on('submit', this.onSubmit, this);
7566         // this was added as random key presses on the form where triggering form submit.
7567         this.el.on('keypress', function(e) {
7568             if (e.getCharCode() != 13) {
7569                 return true;
7570             }
7571             // we might need to allow it for textareas.. and some other items.
7572             // check e.getTarget().
7573
7574             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7575                 return true;
7576             }
7577
7578             Roo.log("keypress blocked");
7579
7580             e.preventDefault();
7581             return false;
7582         });
7583         
7584     },
7585     // private
7586     onSubmit : function(e){
7587         e.stopEvent();
7588     },
7589
7590      /**
7591      * Returns true if client-side validation on the form is successful.
7592      * @return Boolean
7593      */
7594     isValid : function(){
7595         var items = this.getItems();
7596         var valid = true;
7597         var target = false;
7598         
7599         items.each(function(f){
7600             
7601             if(f.validate()){
7602                 return;
7603             }
7604             valid = false;
7605
7606             if(!target && f.el.isVisible(true)){
7607                 target = f;
7608             }
7609            
7610         });
7611         
7612         if(this.errorMask && !valid){
7613             Roo.bootstrap.Form.popover.mask(this, target);
7614         }
7615         
7616         return valid;
7617     },
7618     
7619     /**
7620      * Returns true if any fields in this form have changed since their original load.
7621      * @return Boolean
7622      */
7623     isDirty : function(){
7624         var dirty = false;
7625         var items = this.getItems();
7626         items.each(function(f){
7627            if(f.isDirty()){
7628                dirty = true;
7629                return false;
7630            }
7631            return true;
7632         });
7633         return dirty;
7634     },
7635      /**
7636      * Performs a predefined action (submit or load) or custom actions you define on this form.
7637      * @param {String} actionName The name of the action type
7638      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7639      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7640      * accept other config options):
7641      * <pre>
7642 Property          Type             Description
7643 ----------------  ---------------  ----------------------------------------------------------------------------------
7644 url               String           The url for the action (defaults to the form's url)
7645 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7646 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7647 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7648                                    validate the form on the client (defaults to false)
7649      * </pre>
7650      * @return {BasicForm} this
7651      */
7652     doAction : function(action, options){
7653         if(typeof action == 'string'){
7654             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7655         }
7656         if(this.fireEvent('beforeaction', this, action) !== false){
7657             this.beforeAction(action);
7658             action.run.defer(100, action);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     beforeAction : function(action){
7665         var o = action.options;
7666
7667         if(this.loadMask){
7668             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7669         }
7670         // not really supported yet.. ??
7671
7672         //if(this.waitMsgTarget === true){
7673         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7674         //}else if(this.waitMsgTarget){
7675         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7676         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7677         //}else {
7678         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7679        // }
7680
7681     },
7682
7683     // private
7684     afterAction : function(action, success){
7685         this.activeAction = null;
7686         var o = action.options;
7687
7688         //if(this.waitMsgTarget === true){
7689             this.el.unmask();
7690         //}else if(this.waitMsgTarget){
7691         //    this.waitMsgTarget.unmask();
7692         //}else{
7693         //    Roo.MessageBox.updateProgress(1);
7694         //    Roo.MessageBox.hide();
7695        // }
7696         //
7697         if(success){
7698             if(o.reset){
7699                 this.reset();
7700             }
7701             Roo.callback(o.success, o.scope, [this, action]);
7702             this.fireEvent('actioncomplete', this, action);
7703
7704         }else{
7705
7706             // failure condition..
7707             // we have a scenario where updates need confirming.
7708             // eg. if a locking scenario exists..
7709             // we look for { errors : { needs_confirm : true }} in the response.
7710             if (
7711                 (typeof(action.result) != 'undefined')  &&
7712                 (typeof(action.result.errors) != 'undefined')  &&
7713                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7714            ){
7715                 var _t = this;
7716                 Roo.log("not supported yet");
7717                  /*
7718
7719                 Roo.MessageBox.confirm(
7720                     "Change requires confirmation",
7721                     action.result.errorMsg,
7722                     function(r) {
7723                         if (r != 'yes') {
7724                             return;
7725                         }
7726                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7727                     }
7728
7729                 );
7730                 */
7731
7732
7733                 return;
7734             }
7735
7736             Roo.callback(o.failure, o.scope, [this, action]);
7737             // show an error message if no failed handler is set..
7738             if (!this.hasListener('actionfailed')) {
7739                 Roo.log("need to add dialog support");
7740                 /*
7741                 Roo.MessageBox.alert("Error",
7742                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7743                         action.result.errorMsg :
7744                         "Saving Failed, please check your entries or try again"
7745                 );
7746                 */
7747             }
7748
7749             this.fireEvent('actionfailed', this, action);
7750         }
7751
7752     },
7753     /**
7754      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7755      * @param {String} id The value to search for
7756      * @return Field
7757      */
7758     findField : function(id){
7759         var items = this.getItems();
7760         var field = items.get(id);
7761         if(!field){
7762              items.each(function(f){
7763                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7764                     field = f;
7765                     return false;
7766                 }
7767                 return true;
7768             });
7769         }
7770         return field || null;
7771     },
7772      /**
7773      * Mark fields in this form invalid in bulk.
7774      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7775      * @return {BasicForm} this
7776      */
7777     markInvalid : function(errors){
7778         if(errors instanceof Array){
7779             for(var i = 0, len = errors.length; i < len; i++){
7780                 var fieldError = errors[i];
7781                 var f = this.findField(fieldError.id);
7782                 if(f){
7783                     f.markInvalid(fieldError.msg);
7784                 }
7785             }
7786         }else{
7787             var field, id;
7788             for(id in errors){
7789                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7790                     field.markInvalid(errors[id]);
7791                 }
7792             }
7793         }
7794         //Roo.each(this.childForms || [], function (f) {
7795         //    f.markInvalid(errors);
7796         //});
7797
7798         return this;
7799     },
7800
7801     /**
7802      * Set values for fields in this form in bulk.
7803      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7804      * @return {BasicForm} this
7805      */
7806     setValues : function(values){
7807         if(values instanceof Array){ // array of objects
7808             for(var i = 0, len = values.length; i < len; i++){
7809                 var v = values[i];
7810                 var f = this.findField(v.id);
7811                 if(f){
7812                     f.setValue(v.value);
7813                     if(this.trackResetOnLoad){
7814                         f.originalValue = f.getValue();
7815                     }
7816                 }
7817             }
7818         }else{ // object hash
7819             var field, id;
7820             for(id in values){
7821                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7822
7823                     if (field.setFromData &&
7824                         field.valueField &&
7825                         field.displayField &&
7826                         // combos' with local stores can
7827                         // be queried via setValue()
7828                         // to set their value..
7829                         (field.store && !field.store.isLocal)
7830                         ) {
7831                         // it's a combo
7832                         var sd = { };
7833                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7834                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7835                         field.setFromData(sd);
7836
7837                     } else {
7838                         field.setValue(values[id]);
7839                     }
7840
7841
7842                     if(this.trackResetOnLoad){
7843                         field.originalValue = field.getValue();
7844                     }
7845                 }
7846             }
7847         }
7848
7849         //Roo.each(this.childForms || [], function (f) {
7850         //    f.setValues(values);
7851         //});
7852
7853         return this;
7854     },
7855
7856     /**
7857      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7858      * they are returned as an array.
7859      * @param {Boolean} asString
7860      * @return {Object}
7861      */
7862     getValues : function(asString){
7863         //if (this.childForms) {
7864             // copy values from the child forms
7865         //    Roo.each(this.childForms, function (f) {
7866         //        this.setValues(f.getValues());
7867         //    }, this);
7868         //}
7869
7870
7871
7872         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7873         if(asString === true){
7874             return fs;
7875         }
7876         return Roo.urlDecode(fs);
7877     },
7878
7879     /**
7880      * Returns the fields in this form as an object with key/value pairs.
7881      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7882      * @return {Object}
7883      */
7884     getFieldValues : function(with_hidden)
7885     {
7886         var items = this.getItems();
7887         var ret = {};
7888         items.each(function(f){
7889             if (!f.getName()) {
7890                 return;
7891             }
7892             var v = f.getValue();
7893             if (f.inputType =='radio') {
7894                 if (typeof(ret[f.getName()]) == 'undefined') {
7895                     ret[f.getName()] = ''; // empty..
7896                 }
7897
7898                 if (!f.el.dom.checked) {
7899                     return;
7900
7901                 }
7902                 v = f.el.dom.value;
7903
7904             }
7905
7906             // not sure if this supported any more..
7907             if ((typeof(v) == 'object') && f.getRawValue) {
7908                 v = f.getRawValue() ; // dates..
7909             }
7910             // combo boxes where name != hiddenName...
7911             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7912                 ret[f.name] = f.getRawValue();
7913             }
7914             ret[f.getName()] = v;
7915         });
7916
7917         return ret;
7918     },
7919
7920     /**
7921      * Clears all invalid messages in this form.
7922      * @return {BasicForm} this
7923      */
7924     clearInvalid : function(){
7925         var items = this.getItems();
7926
7927         items.each(function(f){
7928            f.clearInvalid();
7929         });
7930
7931
7932
7933         return this;
7934     },
7935
7936     /**
7937      * Resets this form.
7938      * @return {BasicForm} this
7939      */
7940     reset : function(){
7941         var items = this.getItems();
7942         items.each(function(f){
7943             f.reset();
7944         });
7945
7946         Roo.each(this.childForms || [], function (f) {
7947             f.reset();
7948         });
7949
7950
7951         return this;
7952     },
7953     getItems : function()
7954     {
7955         var r=new Roo.util.MixedCollection(false, function(o){
7956             return o.id || (o.id = Roo.id());
7957         });
7958         var iter = function(el) {
7959             if (el.inputEl) {
7960                 r.add(el);
7961             }
7962             if (!el.items) {
7963                 return;
7964             }
7965             Roo.each(el.items,function(e) {
7966                 iter(e);
7967             });
7968
7969
7970         };
7971
7972         iter(this);
7973         return r;
7974
7975
7976
7977
7978     }
7979
7980 });
7981
7982 Roo.apply(Roo.bootstrap.Form, {
7983     
7984     popover : {
7985         
7986         padding : 5,
7987         
7988         isApplied : false,
7989         
7990         isMasked : false,
7991         
7992         form : false,
7993         
7994         target : false,
7995         
7996         toolTip : false,
7997         
7998         intervalID : false,
7999         
8000         maskEl : false,
8001         
8002         apply : function()
8003         {
8004             if(this.isApplied){
8005                 return;
8006             }
8007             
8008             this.maskEl = {
8009                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8010                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8011                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8012                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8013             };
8014             
8015             this.maskEl.top.enableDisplayMode("block");
8016             this.maskEl.left.enableDisplayMode("block");
8017             this.maskEl.bottom.enableDisplayMode("block");
8018             this.maskEl.right.enableDisplayMode("block");
8019             
8020             this.toolTip = new Roo.bootstrap.Tooltip({
8021                 cls : 'roo-form-error-popover',
8022                 alignment : {
8023                     'left' : ['r-l', [-2,0], 'right'],
8024                     'right' : ['l-r', [2,0], 'left'],
8025                     'bottom' : ['tl-bl', [0,2], 'top'],
8026                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8027                 }
8028             });
8029             
8030             this.toolTip.render(Roo.get(document.body));
8031
8032             this.toolTip.el.enableDisplayMode("block");
8033             
8034             Roo.get(document.body).on('click', function(){
8035                 this.unmask();
8036             }, this);
8037             
8038             this.isApplied = true
8039         },
8040         
8041         mask : function(form, target)
8042         {
8043             this.form = form;
8044             
8045             this.target = target;
8046             
8047             if(!this.form.errorMask || !target.el){
8048                 return;
8049             }
8050             
8051             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8052             
8053             var scrolled = scrollable.getScroll();
8054             
8055             var ot = this.target.el.calcOffsetsTo(scrollable);
8056             
8057             scrollTo = ot[1] - 100;
8058             
8059             scrollable.scrollTo('top', scrollTo);
8060             
8061             var box = this.target.el.getBox();
8062
8063             var zIndex = Roo.bootstrap.Modal.zIndex++;
8064
8065             this.maskEl.top.setStyle('position', 'fixed');
8066             this.maskEl.top.setStyle('z-index', zIndex);
8067             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8068             this.maskEl.top.setXY([0, 0]);
8069             this.maskEl.top.show();
8070
8071             this.maskEl.left.setStyle('position', 'fixed');
8072             this.maskEl.left.setStyle('z-index', zIndex);
8073             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8074             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8075             this.maskEl.left.show();
8076
8077             this.maskEl.bottom.setStyle('position', 'fixed');
8078             this.maskEl.bottom.setStyle('z-index', zIndex);
8079             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8080             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8081             this.maskEl.bottom.show();
8082
8083             this.maskEl.right.setStyle('position', 'fixed');
8084             this.maskEl.right.setStyle('z-index', zIndex);
8085             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8086             this.maskEl.right.setXY([0, box.y - this.padding]);
8087             this.maskEl.right.show();
8088
8089
8090             this.toolTip.bindEl = this.target.el;
8091
8092             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8093
8094             var tip = this.target.blankText;
8095
8096             if(this.target.getValue() !== '' && this.target.regexText.length){
8097                 tip = this.target.regexText;
8098             }
8099
8100             this.toolTip.show(tip);
8101
8102             this.intervalID = window.setInterval(function() {
8103                 Roo.bootstrap.Form.popover.unmask();
8104             }, 10000);
8105
8106             window.onwheel = function(){ return false;};
8107             
8108             (function(){ this.isMasked = true; }).defer(500, this);
8109                 
8110             
8111             
8112         },
8113         
8114         unmask : function()
8115         {
8116             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8117                 return;
8118             }
8119             
8120             this.maskEl.top.setStyle('position', 'absolute');
8121             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8122             this.maskEl.top.hide();
8123
8124             this.maskEl.left.setStyle('position', 'absolute');
8125             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8126             this.maskEl.left.hide();
8127
8128             this.maskEl.bottom.setStyle('position', 'absolute');
8129             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8130             this.maskEl.bottom.hide();
8131
8132             this.maskEl.right.setStyle('position', 'absolute');
8133             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8134             this.maskEl.right.hide();
8135             
8136             this.toolTip.hide();
8137             
8138             this.toolTip.el.hide();
8139             
8140             window.onwheel = function(){ return true;};
8141             
8142             if(this.intervalID){
8143                 window.clearInterval(this.intervalID);
8144                 this.intervalID = false;
8145             }
8146             
8147             this.isMasked = false;
8148             
8149         }
8150         
8151     }
8152     
8153 });
8154
8155 /*
8156  * Based on:
8157  * Ext JS Library 1.1.1
8158  * Copyright(c) 2006-2007, Ext JS, LLC.
8159  *
8160  * Originally Released Under LGPL - original licence link has changed is not relivant.
8161  *
8162  * Fork - LGPL
8163  * <script type="text/javascript">
8164  */
8165 /**
8166  * @class Roo.form.VTypes
8167  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8168  * @singleton
8169  */
8170 Roo.form.VTypes = function(){
8171     // closure these in so they are only created once.
8172     var alpha = /^[a-zA-Z_]+$/;
8173     var alphanum = /^[a-zA-Z0-9_]+$/;
8174     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8175     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8176
8177     // All these messages and functions are configurable
8178     return {
8179         /**
8180          * The function used to validate email addresses
8181          * @param {String} value The email address
8182          */
8183         'email' : function(v){
8184             return email.test(v);
8185         },
8186         /**
8187          * The error text to display when the email validation function returns false
8188          * @type String
8189          */
8190         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8191         /**
8192          * The keystroke filter mask to be applied on email input
8193          * @type RegExp
8194          */
8195         'emailMask' : /[a-z0-9_\.\-@]/i,
8196
8197         /**
8198          * The function used to validate URLs
8199          * @param {String} value The URL
8200          */
8201         'url' : function(v){
8202             return url.test(v);
8203         },
8204         /**
8205          * The error text to display when the url validation function returns false
8206          * @type String
8207          */
8208         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8209         
8210         /**
8211          * The function used to validate alpha values
8212          * @param {String} value The value
8213          */
8214         'alpha' : function(v){
8215             return alpha.test(v);
8216         },
8217         /**
8218          * The error text to display when the alpha validation function returns false
8219          * @type String
8220          */
8221         'alphaText' : 'This field should only contain letters and _',
8222         /**
8223          * The keystroke filter mask to be applied on alpha input
8224          * @type RegExp
8225          */
8226         'alphaMask' : /[a-z_]/i,
8227
8228         /**
8229          * The function used to validate alphanumeric values
8230          * @param {String} value The value
8231          */
8232         'alphanum' : function(v){
8233             return alphanum.test(v);
8234         },
8235         /**
8236          * The error text to display when the alphanumeric validation function returns false
8237          * @type String
8238          */
8239         'alphanumText' : 'This field should only contain letters, numbers and _',
8240         /**
8241          * The keystroke filter mask to be applied on alphanumeric input
8242          * @type RegExp
8243          */
8244         'alphanumMask' : /[a-z0-9_]/i
8245     };
8246 }();/*
8247  * - LGPL
8248  *
8249  * Input
8250  * 
8251  */
8252
8253 /**
8254  * @class Roo.bootstrap.Input
8255  * @extends Roo.bootstrap.Component
8256  * Bootstrap Input class
8257  * @cfg {Boolean} disabled is it disabled
8258  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8259  * @cfg {String} name name of the input
8260  * @cfg {string} fieldLabel - the label associated
8261  * @cfg {string} placeholder - placeholder to put in text.
8262  * @cfg {string}  before - input group add on before
8263  * @cfg {string} after - input group add on after
8264  * @cfg {string} size - (lg|sm) or leave empty..
8265  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8266  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8267  * @cfg {Number} md colspan out of 12 for computer-sized screens
8268  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8269  * @cfg {string} value default value of the input
8270  * @cfg {Number} labelWidth set the width of label 
8271  * @cfg {Number} labellg set the width of label (1-12)
8272  * @cfg {Number} labelmd set the width of label (1-12)
8273  * @cfg {Number} labelsm set the width of label (1-12)
8274  * @cfg {Number} labelxs set the width of label (1-12)
8275  * @cfg {String} labelAlign (top|left)
8276  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8277  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8278  * @cfg {String} indicatorpos (left|right) default left
8279
8280  * @cfg {String} align (left|center|right) Default left
8281  * @cfg {Boolean} forceFeedback (true|false) Default false
8282  * 
8283  * 
8284  * 
8285  * 
8286  * @constructor
8287  * Create a new Input
8288  * @param {Object} config The config object
8289  */
8290
8291 Roo.bootstrap.Input = function(config){
8292     
8293     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8294     
8295     this.addEvents({
8296         /**
8297          * @event focus
8298          * Fires when this field receives input focus.
8299          * @param {Roo.form.Field} this
8300          */
8301         focus : true,
8302         /**
8303          * @event blur
8304          * Fires when this field loses input focus.
8305          * @param {Roo.form.Field} this
8306          */
8307         blur : true,
8308         /**
8309          * @event specialkey
8310          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8311          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8312          * @param {Roo.form.Field} this
8313          * @param {Roo.EventObject} e The event object
8314          */
8315         specialkey : true,
8316         /**
8317          * @event change
8318          * Fires just before the field blurs if the field value has changed.
8319          * @param {Roo.form.Field} this
8320          * @param {Mixed} newValue The new value
8321          * @param {Mixed} oldValue The original value
8322          */
8323         change : true,
8324         /**
8325          * @event invalid
8326          * Fires after the field has been marked as invalid.
8327          * @param {Roo.form.Field} this
8328          * @param {String} msg The validation message
8329          */
8330         invalid : true,
8331         /**
8332          * @event valid
8333          * Fires after the field has been validated with no errors.
8334          * @param {Roo.form.Field} this
8335          */
8336         valid : true,
8337          /**
8338          * @event keyup
8339          * Fires after the key up
8340          * @param {Roo.form.Field} this
8341          * @param {Roo.EventObject}  e The event Object
8342          */
8343         keyup : true
8344     });
8345 };
8346
8347 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8348      /**
8349      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8350       automatic validation (defaults to "keyup").
8351      */
8352     validationEvent : "keyup",
8353      /**
8354      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8355      */
8356     validateOnBlur : true,
8357     /**
8358      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8359      */
8360     validationDelay : 250,
8361      /**
8362      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8363      */
8364     focusClass : "x-form-focus",  // not needed???
8365     
8366        
8367     /**
8368      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8369      */
8370     invalidClass : "has-warning",
8371     
8372     /**
8373      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8374      */
8375     validClass : "has-success",
8376     
8377     /**
8378      * @cfg {Boolean} hasFeedback (true|false) default true
8379      */
8380     hasFeedback : true,
8381     
8382     /**
8383      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8384      */
8385     invalidFeedbackClass : "glyphicon-warning-sign",
8386     
8387     /**
8388      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8389      */
8390     validFeedbackClass : "glyphicon-ok",
8391     
8392     /**
8393      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8394      */
8395     selectOnFocus : false,
8396     
8397      /**
8398      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8399      */
8400     maskRe : null,
8401        /**
8402      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8403      */
8404     vtype : null,
8405     
8406       /**
8407      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8408      */
8409     disableKeyFilter : false,
8410     
8411        /**
8412      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8413      */
8414     disabled : false,
8415      /**
8416      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8417      */
8418     allowBlank : true,
8419     /**
8420      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8421      */
8422     blankText : "Please complete this mandatory field",
8423     
8424      /**
8425      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8426      */
8427     minLength : 0,
8428     /**
8429      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8430      */
8431     maxLength : Number.MAX_VALUE,
8432     /**
8433      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8434      */
8435     minLengthText : "The minimum length for this field is {0}",
8436     /**
8437      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8438      */
8439     maxLengthText : "The maximum length for this field is {0}",
8440   
8441     
8442     /**
8443      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8444      * If available, this function will be called only after the basic validators all return true, and will be passed the
8445      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8446      */
8447     validator : null,
8448     /**
8449      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8450      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8451      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8452      */
8453     regex : null,
8454     /**
8455      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8456      */
8457     regexText : "",
8458     
8459     autocomplete: false,
8460     
8461     
8462     fieldLabel : '',
8463     inputType : 'text',
8464     
8465     name : false,
8466     placeholder: false,
8467     before : false,
8468     after : false,
8469     size : false,
8470     hasFocus : false,
8471     preventMark: false,
8472     isFormField : true,
8473     value : '',
8474     labelWidth : 2,
8475     labelAlign : false,
8476     readOnly : false,
8477     align : false,
8478     formatedValue : false,
8479     forceFeedback : false,
8480     
8481     indicatorpos : 'left',
8482     
8483     labellg : 0,
8484     labelmd : 0,
8485     labelsm : 0,
8486     labelxs : 0,
8487     
8488     parentLabelAlign : function()
8489     {
8490         var parent = this;
8491         while (parent.parent()) {
8492             parent = parent.parent();
8493             if (typeof(parent.labelAlign) !='undefined') {
8494                 return parent.labelAlign;
8495             }
8496         }
8497         return 'left';
8498         
8499     },
8500     
8501     getAutoCreate : function()
8502     {
8503         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8504         
8505         var id = Roo.id();
8506         
8507         var cfg = {};
8508         
8509         if(this.inputType != 'hidden'){
8510             cfg.cls = 'form-group' //input-group
8511         }
8512         
8513         var input =  {
8514             tag: 'input',
8515             id : id,
8516             type : this.inputType,
8517             value : this.value,
8518             cls : 'form-control',
8519             placeholder : this.placeholder || '',
8520             autocomplete : this.autocomplete || 'new-password'
8521         };
8522         
8523         if(this.align){
8524             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8525         }
8526         
8527         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8528             input.maxLength = this.maxLength;
8529         }
8530         
8531         if (this.disabled) {
8532             input.disabled=true;
8533         }
8534         
8535         if (this.readOnly) {
8536             input.readonly=true;
8537         }
8538         
8539         if (this.name) {
8540             input.name = this.name;
8541         }
8542         
8543         if (this.size) {
8544             input.cls += ' input-' + this.size;
8545         }
8546         
8547         var settings=this;
8548         ['xs','sm','md','lg'].map(function(size){
8549             if (settings[size]) {
8550                 cfg.cls += ' col-' + size + '-' + settings[size];
8551             }
8552         });
8553         
8554         var inputblock = input;
8555         
8556         var feedback = {
8557             tag: 'span',
8558             cls: 'glyphicon form-control-feedback'
8559         };
8560             
8561         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8562             
8563             inputblock = {
8564                 cls : 'has-feedback',
8565                 cn :  [
8566                     input,
8567                     feedback
8568                 ] 
8569             };  
8570         }
8571         
8572         if (this.before || this.after) {
8573             
8574             inputblock = {
8575                 cls : 'input-group',
8576                 cn :  [] 
8577             };
8578             
8579             if (this.before && typeof(this.before) == 'string') {
8580                 
8581                 inputblock.cn.push({
8582                     tag :'span',
8583                     cls : 'roo-input-before input-group-addon',
8584                     html : this.before
8585                 });
8586             }
8587             if (this.before && typeof(this.before) == 'object') {
8588                 this.before = Roo.factory(this.before);
8589                 
8590                 inputblock.cn.push({
8591                     tag :'span',
8592                     cls : 'roo-input-before input-group-' +
8593                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8594                 });
8595             }
8596             
8597             inputblock.cn.push(input);
8598             
8599             if (this.after && typeof(this.after) == 'string') {
8600                 inputblock.cn.push({
8601                     tag :'span',
8602                     cls : 'roo-input-after input-group-addon',
8603                     html : this.after
8604                 });
8605             }
8606             if (this.after && typeof(this.after) == 'object') {
8607                 this.after = Roo.factory(this.after);
8608                 
8609                 inputblock.cn.push({
8610                     tag :'span',
8611                     cls : 'roo-input-after input-group-' +
8612                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8613                 });
8614             }
8615             
8616             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8617                 inputblock.cls += ' has-feedback';
8618                 inputblock.cn.push(feedback);
8619             }
8620         };
8621         
8622         if (align ==='left' && this.fieldLabel.length) {
8623             
8624             cfg.cls += ' roo-form-group-label-left';
8625             
8626             cfg.cn = [
8627                 {
8628                     tag : 'i',
8629                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8630                     tooltip : 'This field is required'
8631                 },
8632                 {
8633                     tag: 'label',
8634                     'for' :  id,
8635                     cls : 'control-label',
8636                     html : this.fieldLabel
8637
8638                 },
8639                 {
8640                     cls : "", 
8641                     cn: [
8642                         inputblock
8643                     ]
8644                 }
8645             ];
8646             
8647             var labelCfg = cfg.cn[1];
8648             var contentCfg = cfg.cn[2];
8649             
8650             if(this.indicatorpos == 'right'){
8651                 cfg.cn = [
8652                     {
8653                         tag: 'label',
8654                         'for' :  id,
8655                         cls : 'control-label',
8656                         html : this.fieldLabel
8657
8658                     },
8659                     {
8660                         tag : 'i',
8661                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8662                         tooltip : 'This field is required'
8663                     },
8664                     {
8665                         cls : "",
8666                         cn: [
8667                             inputblock
8668                         ]
8669                     }
8670
8671                 ];
8672                 
8673                 labelCfg = cfg.cn[0];
8674                 contentCfg = cfg.cn[2];
8675             
8676             }
8677             
8678             if(this.labelWidth > 12){
8679                 labelCfg.style = "width: " + this.labelWidth + 'px';
8680             }
8681             
8682             if(this.labelWidth < 13 && this.labelmd == 0){
8683                 this.labelmd = this.labelWidth;
8684             }
8685             
8686             if(this.labellg > 0){
8687                 labelCfg.cls += ' col-lg-' + this.labellg;
8688                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8689             }
8690             
8691             if(this.labelmd > 0){
8692                 labelCfg.cls += ' col-md-' + this.labelmd;
8693                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8694             }
8695             
8696             if(this.labelsm > 0){
8697                 labelCfg.cls += ' col-sm-' + this.labelsm;
8698                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8699             }
8700             
8701             if(this.labelxs > 0){
8702                 labelCfg.cls += ' col-xs-' + this.labelxs;
8703                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8704             }
8705             
8706             
8707         } else if ( this.fieldLabel.length) {
8708                 
8709             cfg.cn = [
8710                 {
8711                     tag : 'i',
8712                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8713                     tooltip : 'This field is required'
8714                 },
8715                 {
8716                     tag: 'label',
8717                    //cls : 'input-group-addon',
8718                     html : this.fieldLabel
8719
8720                 },
8721
8722                inputblock
8723
8724            ];
8725            
8726            if(this.indicatorpos == 'right'){
8727                 
8728                 cfg.cn = [
8729                     {
8730                         tag: 'label',
8731                        //cls : 'input-group-addon',
8732                         html : this.fieldLabel
8733
8734                     },
8735                     {
8736                         tag : 'i',
8737                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8738                         tooltip : 'This field is required'
8739                     },
8740
8741                    inputblock
8742
8743                ];
8744
8745             }
8746
8747         } else {
8748             
8749             cfg.cn = [
8750
8751                     inputblock
8752
8753             ];
8754                 
8755                 
8756         };
8757         
8758         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8759            cfg.cls += ' navbar-form';
8760         }
8761         
8762         if (this.parentType === 'NavGroup') {
8763            cfg.cls += ' navbar-form';
8764            cfg.tag = 'li';
8765         }
8766         
8767         return cfg;
8768         
8769     },
8770     /**
8771      * return the real input element.
8772      */
8773     inputEl: function ()
8774     {
8775         return this.el.select('input.form-control',true).first();
8776     },
8777     
8778     tooltipEl : function()
8779     {
8780         return this.inputEl();
8781     },
8782     
8783     indicatorEl : function()
8784     {
8785         var indicator = this.el.select('i.roo-required-indicator',true).first();
8786         
8787         if(!indicator){
8788             return false;
8789         }
8790         
8791         return indicator;
8792         
8793     },
8794     
8795     setDisabled : function(v)
8796     {
8797         var i  = this.inputEl().dom;
8798         if (!v) {
8799             i.removeAttribute('disabled');
8800             return;
8801             
8802         }
8803         i.setAttribute('disabled','true');
8804     },
8805     initEvents : function()
8806     {
8807           
8808         this.inputEl().on("keydown" , this.fireKey,  this);
8809         this.inputEl().on("focus", this.onFocus,  this);
8810         this.inputEl().on("blur", this.onBlur,  this);
8811         
8812         this.inputEl().relayEvent('keyup', this);
8813         
8814         this.indicator = this.indicatorEl();
8815         
8816         if(this.indicator){
8817             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8818             this.indicator.hide();
8819         }
8820  
8821         // reference to original value for reset
8822         this.originalValue = this.getValue();
8823         //Roo.form.TextField.superclass.initEvents.call(this);
8824         if(this.validationEvent == 'keyup'){
8825             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8826             this.inputEl().on('keyup', this.filterValidation, this);
8827         }
8828         else if(this.validationEvent !== false){
8829             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8830         }
8831         
8832         if(this.selectOnFocus){
8833             this.on("focus", this.preFocus, this);
8834             
8835         }
8836         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8837             this.inputEl().on("keypress", this.filterKeys, this);
8838         } else {
8839             this.inputEl().relayEvent('keypress', this);
8840         }
8841        /* if(this.grow){
8842             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8843             this.el.on("click", this.autoSize,  this);
8844         }
8845         */
8846         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8847             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8848         }
8849         
8850         if (typeof(this.before) == 'object') {
8851             this.before.render(this.el.select('.roo-input-before',true).first());
8852         }
8853         if (typeof(this.after) == 'object') {
8854             this.after.render(this.el.select('.roo-input-after',true).first());
8855         }
8856         
8857         
8858     },
8859     filterValidation : function(e){
8860         if(!e.isNavKeyPress()){
8861             this.validationTask.delay(this.validationDelay);
8862         }
8863     },
8864      /**
8865      * Validates the field value
8866      * @return {Boolean} True if the value is valid, else false
8867      */
8868     validate : function(){
8869         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8870         if(this.disabled || this.validateValue(this.getRawValue())){
8871             this.markValid();
8872             return true;
8873         }
8874         
8875         this.markInvalid();
8876         return false;
8877     },
8878     
8879     
8880     /**
8881      * Validates a value according to the field's validation rules and marks the field as invalid
8882      * if the validation fails
8883      * @param {Mixed} value The value to validate
8884      * @return {Boolean} True if the value is valid, else false
8885      */
8886     validateValue : function(value){
8887         if(value.length < 1)  { // if it's blank
8888             if(this.allowBlank){
8889                 return true;
8890             }
8891             return false;
8892         }
8893         
8894         if(value.length < this.minLength){
8895             return false;
8896         }
8897         if(value.length > this.maxLength){
8898             return false;
8899         }
8900         if(this.vtype){
8901             var vt = Roo.form.VTypes;
8902             if(!vt[this.vtype](value, this)){
8903                 return false;
8904             }
8905         }
8906         if(typeof this.validator == "function"){
8907             var msg = this.validator(value);
8908             if(msg !== true){
8909                 return false;
8910             }
8911         }
8912         
8913         if(this.regex && !this.regex.test(value)){
8914             return false;
8915         }
8916         
8917         return true;
8918     },
8919
8920     
8921     
8922      // private
8923     fireKey : function(e){
8924         //Roo.log('field ' + e.getKey());
8925         if(e.isNavKeyPress()){
8926             this.fireEvent("specialkey", this, e);
8927         }
8928     },
8929     focus : function (selectText){
8930         if(this.rendered){
8931             this.inputEl().focus();
8932             if(selectText === true){
8933                 this.inputEl().dom.select();
8934             }
8935         }
8936         return this;
8937     } ,
8938     
8939     onFocus : function(){
8940         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8941            // this.el.addClass(this.focusClass);
8942         }
8943         if(!this.hasFocus){
8944             this.hasFocus = true;
8945             this.startValue = this.getValue();
8946             this.fireEvent("focus", this);
8947         }
8948     },
8949     
8950     beforeBlur : Roo.emptyFn,
8951
8952     
8953     // private
8954     onBlur : function(){
8955         this.beforeBlur();
8956         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8957             //this.el.removeClass(this.focusClass);
8958         }
8959         this.hasFocus = false;
8960         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8961             this.validate();
8962         }
8963         var v = this.getValue();
8964         if(String(v) !== String(this.startValue)){
8965             this.fireEvent('change', this, v, this.startValue);
8966         }
8967         this.fireEvent("blur", this);
8968     },
8969     
8970     /**
8971      * Resets the current field value to the originally loaded value and clears any validation messages
8972      */
8973     reset : function(){
8974         this.setValue(this.originalValue);
8975         this.validate();
8976     },
8977      /**
8978      * Returns the name of the field
8979      * @return {Mixed} name The name field
8980      */
8981     getName: function(){
8982         return this.name;
8983     },
8984      /**
8985      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8986      * @return {Mixed} value The field value
8987      */
8988     getValue : function(){
8989         
8990         var v = this.inputEl().getValue();
8991         
8992         return v;
8993     },
8994     /**
8995      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8996      * @return {Mixed} value The field value
8997      */
8998     getRawValue : function(){
8999         var v = this.inputEl().getValue();
9000         
9001         return v;
9002     },
9003     
9004     /**
9005      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9006      * @param {Mixed} value The value to set
9007      */
9008     setRawValue : function(v){
9009         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9010     },
9011     
9012     selectText : function(start, end){
9013         var v = this.getRawValue();
9014         if(v.length > 0){
9015             start = start === undefined ? 0 : start;
9016             end = end === undefined ? v.length : end;
9017             var d = this.inputEl().dom;
9018             if(d.setSelectionRange){
9019                 d.setSelectionRange(start, end);
9020             }else if(d.createTextRange){
9021                 var range = d.createTextRange();
9022                 range.moveStart("character", start);
9023                 range.moveEnd("character", v.length-end);
9024                 range.select();
9025             }
9026         }
9027     },
9028     
9029     /**
9030      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9031      * @param {Mixed} value The value to set
9032      */
9033     setValue : function(v){
9034         this.value = v;
9035         if(this.rendered){
9036             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9037             this.validate();
9038         }
9039     },
9040     
9041     /*
9042     processValue : function(value){
9043         if(this.stripCharsRe){
9044             var newValue = value.replace(this.stripCharsRe, '');
9045             if(newValue !== value){
9046                 this.setRawValue(newValue);
9047                 return newValue;
9048             }
9049         }
9050         return value;
9051     },
9052   */
9053     preFocus : function(){
9054         
9055         if(this.selectOnFocus){
9056             this.inputEl().dom.select();
9057         }
9058     },
9059     filterKeys : function(e){
9060         var k = e.getKey();
9061         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9062             return;
9063         }
9064         var c = e.getCharCode(), cc = String.fromCharCode(c);
9065         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9066             return;
9067         }
9068         if(!this.maskRe.test(cc)){
9069             e.stopEvent();
9070         }
9071     },
9072      /**
9073      * Clear any invalid styles/messages for this field
9074      */
9075     clearInvalid : function(){
9076         
9077         if(!this.el || this.preventMark){ // not rendered
9078             return;
9079         }
9080         
9081         if(this.indicator){
9082             this.indicator.hide();
9083         }
9084         
9085         this.el.removeClass(this.invalidClass);
9086         
9087         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9088             
9089             var feedback = this.el.select('.form-control-feedback', true).first();
9090             
9091             if(feedback){
9092                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9093             }
9094             
9095         }
9096         
9097         this.fireEvent('valid', this);
9098     },
9099     
9100      /**
9101      * Mark this field as valid
9102      */
9103     markValid : function()
9104     {
9105         if(!this.el  || this.preventMark){ // not rendered...
9106             return;
9107         }
9108         
9109         this.el.removeClass([this.invalidClass, this.validClass]);
9110         
9111         var feedback = this.el.select('.form-control-feedback', true).first();
9112             
9113         if(feedback){
9114             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9115         }
9116
9117         if(this.disabled){
9118             return;
9119         }
9120         
9121         if(this.allowBlank && !this.getRawValue().length){
9122             return;
9123         }
9124         
9125         if(this.indicator){
9126             this.indicator.hide();
9127         }
9128         
9129         this.el.addClass(this.validClass);
9130         
9131         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9132             
9133             var feedback = this.el.select('.form-control-feedback', true).first();
9134             
9135             if(feedback){
9136                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9137                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9138             }
9139             
9140         }
9141         
9142         this.fireEvent('valid', this);
9143     },
9144     
9145      /**
9146      * Mark this field as invalid
9147      * @param {String} msg The validation message
9148      */
9149     markInvalid : function(msg)
9150     {
9151         if(!this.el  || this.preventMark){ // not rendered
9152             return;
9153         }
9154         
9155         this.el.removeClass([this.invalidClass, this.validClass]);
9156         
9157         var feedback = this.el.select('.form-control-feedback', true).first();
9158             
9159         if(feedback){
9160             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9161         }
9162
9163         if(this.disabled){
9164             return;
9165         }
9166         
9167         if(this.allowBlank && !this.getRawValue().length){
9168             return;
9169         }
9170         
9171         if(this.indicator){
9172             this.indicator.show();
9173         }
9174         
9175         this.el.addClass(this.invalidClass);
9176         
9177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9178             
9179             var feedback = this.el.select('.form-control-feedback', true).first();
9180             
9181             if(feedback){
9182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9183                 
9184                 if(this.getValue().length || this.forceFeedback){
9185                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9186                 }
9187                 
9188             }
9189             
9190         }
9191         
9192         this.fireEvent('invalid', this, msg);
9193     },
9194     // private
9195     SafariOnKeyDown : function(event)
9196     {
9197         // this is a workaround for a password hang bug on chrome/ webkit.
9198         if (this.inputEl().dom.type != 'password') {
9199             return;
9200         }
9201         
9202         var isSelectAll = false;
9203         
9204         if(this.inputEl().dom.selectionEnd > 0){
9205             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9206         }
9207         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9208             event.preventDefault();
9209             this.setValue('');
9210             return;
9211         }
9212         
9213         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9214             
9215             event.preventDefault();
9216             // this is very hacky as keydown always get's upper case.
9217             //
9218             var cc = String.fromCharCode(event.getCharCode());
9219             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9220             
9221         }
9222     },
9223     adjustWidth : function(tag, w){
9224         tag = tag.toLowerCase();
9225         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9226             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9227                 if(tag == 'input'){
9228                     return w + 2;
9229                 }
9230                 if(tag == 'textarea'){
9231                     return w-2;
9232                 }
9233             }else if(Roo.isOpera){
9234                 if(tag == 'input'){
9235                     return w + 2;
9236                 }
9237                 if(tag == 'textarea'){
9238                     return w-2;
9239                 }
9240             }
9241         }
9242         return w;
9243     }
9244     
9245 });
9246
9247  
9248 /*
9249  * - LGPL
9250  *
9251  * Input
9252  * 
9253  */
9254
9255 /**
9256  * @class Roo.bootstrap.TextArea
9257  * @extends Roo.bootstrap.Input
9258  * Bootstrap TextArea class
9259  * @cfg {Number} cols Specifies the visible width of a text area
9260  * @cfg {Number} rows Specifies the visible number of lines in a text area
9261  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9262  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9263  * @cfg {string} html text
9264  * 
9265  * @constructor
9266  * Create a new TextArea
9267  * @param {Object} config The config object
9268  */
9269
9270 Roo.bootstrap.TextArea = function(config){
9271     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9272    
9273 };
9274
9275 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9276      
9277     cols : false,
9278     rows : 5,
9279     readOnly : false,
9280     warp : 'soft',
9281     resize : false,
9282     value: false,
9283     html: false,
9284     
9285     getAutoCreate : function(){
9286         
9287         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9288         
9289         var id = Roo.id();
9290         
9291         var cfg = {};
9292         
9293         var input =  {
9294             tag: 'textarea',
9295             id : id,
9296             warp : this.warp,
9297             rows : this.rows,
9298             value : this.value || '',
9299             html: this.html || '',
9300             cls : 'form-control',
9301             placeholder : this.placeholder || '' 
9302             
9303         };
9304         
9305         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9306             input.maxLength = this.maxLength;
9307         }
9308         
9309         if(this.resize){
9310             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9311         }
9312         
9313         if(this.cols){
9314             input.cols = this.cols;
9315         }
9316         
9317         if (this.readOnly) {
9318             input.readonly = true;
9319         }
9320         
9321         if (this.name) {
9322             input.name = this.name;
9323         }
9324         
9325         if (this.size) {
9326             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9327         }
9328         
9329         var settings=this;
9330         ['xs','sm','md','lg'].map(function(size){
9331             if (settings[size]) {
9332                 cfg.cls += ' col-' + size + '-' + settings[size];
9333             }
9334         });
9335         
9336         var inputblock = input;
9337         
9338         if(this.hasFeedback && !this.allowBlank){
9339             
9340             var feedback = {
9341                 tag: 'span',
9342                 cls: 'glyphicon form-control-feedback'
9343             };
9344
9345             inputblock = {
9346                 cls : 'has-feedback',
9347                 cn :  [
9348                     input,
9349                     feedback
9350                 ] 
9351             };  
9352         }
9353         
9354         
9355         if (this.before || this.after) {
9356             
9357             inputblock = {
9358                 cls : 'input-group',
9359                 cn :  [] 
9360             };
9361             if (this.before) {
9362                 inputblock.cn.push({
9363                     tag :'span',
9364                     cls : 'input-group-addon',
9365                     html : this.before
9366                 });
9367             }
9368             
9369             inputblock.cn.push(input);
9370             
9371             if(this.hasFeedback && !this.allowBlank){
9372                 inputblock.cls += ' has-feedback';
9373                 inputblock.cn.push(feedback);
9374             }
9375             
9376             if (this.after) {
9377                 inputblock.cn.push({
9378                     tag :'span',
9379                     cls : 'input-group-addon',
9380                     html : this.after
9381                 });
9382             }
9383             
9384         }
9385         
9386         if (align ==='left' && this.fieldLabel.length) {
9387             cfg.cn = [
9388                 {
9389                     tag: 'label',
9390                     'for' :  id,
9391                     cls : 'control-label',
9392                     html : this.fieldLabel
9393                 },
9394                 {
9395                     cls : "",
9396                     cn: [
9397                         inputblock
9398                     ]
9399                 }
9400
9401             ];
9402             
9403             if(this.labelWidth > 12){
9404                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9405             }
9406
9407             if(this.labelWidth < 13 && this.labelmd == 0){
9408                 this.labelmd = this.labelWidth;
9409             }
9410
9411             if(this.labellg > 0){
9412                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9413                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9414             }
9415
9416             if(this.labelmd > 0){
9417                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9418                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9419             }
9420
9421             if(this.labelsm > 0){
9422                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9423                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9424             }
9425
9426             if(this.labelxs > 0){
9427                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9428                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9429             }
9430             
9431         } else if ( this.fieldLabel.length) {
9432             cfg.cn = [
9433
9434                {
9435                    tag: 'label',
9436                    //cls : 'input-group-addon',
9437                    html : this.fieldLabel
9438
9439                },
9440
9441                inputblock
9442
9443            ];
9444
9445         } else {
9446
9447             cfg.cn = [
9448
9449                 inputblock
9450
9451             ];
9452                 
9453         }
9454         
9455         if (this.disabled) {
9456             input.disabled=true;
9457         }
9458         
9459         return cfg;
9460         
9461     },
9462     /**
9463      * return the real textarea element.
9464      */
9465     inputEl: function ()
9466     {
9467         return this.el.select('textarea.form-control',true).first();
9468     },
9469     
9470     /**
9471      * Clear any invalid styles/messages for this field
9472      */
9473     clearInvalid : function()
9474     {
9475         
9476         if(!this.el || this.preventMark){ // not rendered
9477             return;
9478         }
9479         
9480         var label = this.el.select('label', true).first();
9481         var icon = this.el.select('i.fa-star', true).first();
9482         
9483         if(label && icon){
9484             icon.remove();
9485         }
9486         
9487         this.el.removeClass(this.invalidClass);
9488         
9489         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9490             
9491             var feedback = this.el.select('.form-control-feedback', true).first();
9492             
9493             if(feedback){
9494                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9495             }
9496             
9497         }
9498         
9499         this.fireEvent('valid', this);
9500     },
9501     
9502      /**
9503      * Mark this field as valid
9504      */
9505     markValid : function()
9506     {
9507         if(!this.el  || this.preventMark){ // not rendered
9508             return;
9509         }
9510         
9511         this.el.removeClass([this.invalidClass, this.validClass]);
9512         
9513         var feedback = this.el.select('.form-control-feedback', true).first();
9514             
9515         if(feedback){
9516             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9517         }
9518
9519         if(this.disabled || this.allowBlank){
9520             return;
9521         }
9522         
9523         var label = this.el.select('label', true).first();
9524         var icon = this.el.select('i.fa-star', true).first();
9525         
9526         if(label && icon){
9527             icon.remove();
9528         }
9529         
9530         this.el.addClass(this.validClass);
9531         
9532         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9533             
9534             var feedback = this.el.select('.form-control-feedback', true).first();
9535             
9536             if(feedback){
9537                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9538                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9539             }
9540             
9541         }
9542         
9543         this.fireEvent('valid', this);
9544     },
9545     
9546      /**
9547      * Mark this field as invalid
9548      * @param {String} msg The validation message
9549      */
9550     markInvalid : function(msg)
9551     {
9552         if(!this.el  || this.preventMark){ // not rendered
9553             return;
9554         }
9555         
9556         this.el.removeClass([this.invalidClass, this.validClass]);
9557         
9558         var feedback = this.el.select('.form-control-feedback', true).first();
9559             
9560         if(feedback){
9561             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9562         }
9563
9564         if(this.disabled || this.allowBlank){
9565             return;
9566         }
9567         
9568         var label = this.el.select('label', true).first();
9569         var icon = this.el.select('i.fa-star', true).first();
9570         
9571         if(!this.getValue().length && label && !icon){
9572             this.el.createChild({
9573                 tag : 'i',
9574                 cls : 'text-danger fa fa-lg fa-star',
9575                 tooltip : 'This field is required',
9576                 style : 'margin-right:5px;'
9577             }, label, true);
9578         }
9579
9580         this.el.addClass(this.invalidClass);
9581         
9582         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9583             
9584             var feedback = this.el.select('.form-control-feedback', true).first();
9585             
9586             if(feedback){
9587                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9588                 
9589                 if(this.getValue().length || this.forceFeedback){
9590                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9591                 }
9592                 
9593             }
9594             
9595         }
9596         
9597         this.fireEvent('invalid', this, msg);
9598     }
9599 });
9600
9601  
9602 /*
9603  * - LGPL
9604  *
9605  * trigger field - base class for combo..
9606  * 
9607  */
9608  
9609 /**
9610  * @class Roo.bootstrap.TriggerField
9611  * @extends Roo.bootstrap.Input
9612  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9613  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9614  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9615  * for which you can provide a custom implementation.  For example:
9616  * <pre><code>
9617 var trigger = new Roo.bootstrap.TriggerField();
9618 trigger.onTriggerClick = myTriggerFn;
9619 trigger.applyTo('my-field');
9620 </code></pre>
9621  *
9622  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9623  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9624  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9625  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9626  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9627
9628  * @constructor
9629  * Create a new TriggerField.
9630  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9631  * to the base TextField)
9632  */
9633 Roo.bootstrap.TriggerField = function(config){
9634     this.mimicing = false;
9635     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9636 };
9637
9638 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9639     /**
9640      * @cfg {String} triggerClass A CSS class to apply to the trigger
9641      */
9642      /**
9643      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9644      */
9645     hideTrigger:false,
9646
9647     /**
9648      * @cfg {Boolean} removable (true|false) special filter default false
9649      */
9650     removable : false,
9651     
9652     /** @cfg {Boolean} grow @hide */
9653     /** @cfg {Number} growMin @hide */
9654     /** @cfg {Number} growMax @hide */
9655
9656     /**
9657      * @hide 
9658      * @method
9659      */
9660     autoSize: Roo.emptyFn,
9661     // private
9662     monitorTab : true,
9663     // private
9664     deferHeight : true,
9665
9666     
9667     actionMode : 'wrap',
9668     
9669     caret : false,
9670     
9671     
9672     getAutoCreate : function(){
9673        
9674         var align = this.labelAlign || this.parentLabelAlign();
9675         
9676         var id = Roo.id();
9677         
9678         var cfg = {
9679             cls: 'form-group' //input-group
9680         };
9681         
9682         
9683         var input =  {
9684             tag: 'input',
9685             id : id,
9686             type : this.inputType,
9687             cls : 'form-control',
9688             autocomplete: 'new-password',
9689             placeholder : this.placeholder || '' 
9690             
9691         };
9692         if (this.name) {
9693             input.name = this.name;
9694         }
9695         if (this.size) {
9696             input.cls += ' input-' + this.size;
9697         }
9698         
9699         if (this.disabled) {
9700             input.disabled=true;
9701         }
9702         
9703         var inputblock = input;
9704         
9705         if(this.hasFeedback && !this.allowBlank){
9706             
9707             var feedback = {
9708                 tag: 'span',
9709                 cls: 'glyphicon form-control-feedback'
9710             };
9711             
9712             if(this.removable && !this.editable && !this.tickable){
9713                 inputblock = {
9714                     cls : 'has-feedback',
9715                     cn :  [
9716                         inputblock,
9717                         {
9718                             tag: 'button',
9719                             html : 'x',
9720                             cls : 'roo-combo-removable-btn close'
9721                         },
9722                         feedback
9723                     ] 
9724                 };
9725             } else {
9726                 inputblock = {
9727                     cls : 'has-feedback',
9728                     cn :  [
9729                         inputblock,
9730                         feedback
9731                     ] 
9732                 };
9733             }
9734
9735         } else {
9736             if(this.removable && !this.editable && !this.tickable){
9737                 inputblock = {
9738                     cls : 'roo-removable',
9739                     cn :  [
9740                         inputblock,
9741                         {
9742                             tag: 'button',
9743                             html : 'x',
9744                             cls : 'roo-combo-removable-btn close'
9745                         }
9746                     ] 
9747                 };
9748             }
9749         }
9750         
9751         if (this.before || this.after) {
9752             
9753             inputblock = {
9754                 cls : 'input-group',
9755                 cn :  [] 
9756             };
9757             if (this.before) {
9758                 inputblock.cn.push({
9759                     tag :'span',
9760                     cls : 'input-group-addon',
9761                     html : this.before
9762                 });
9763             }
9764             
9765             inputblock.cn.push(input);
9766             
9767             if(this.hasFeedback && !this.allowBlank){
9768                 inputblock.cls += ' has-feedback';
9769                 inputblock.cn.push(feedback);
9770             }
9771             
9772             if (this.after) {
9773                 inputblock.cn.push({
9774                     tag :'span',
9775                     cls : 'input-group-addon',
9776                     html : this.after
9777                 });
9778             }
9779             
9780         };
9781         
9782         var box = {
9783             tag: 'div',
9784             cn: [
9785                 {
9786                     tag: 'input',
9787                     type : 'hidden',
9788                     cls: 'form-hidden-field'
9789                 },
9790                 inputblock
9791             ]
9792             
9793         };
9794         
9795         if(this.multiple){
9796             box = {
9797                 tag: 'div',
9798                 cn: [
9799                     {
9800                         tag: 'input',
9801                         type : 'hidden',
9802                         cls: 'form-hidden-field'
9803                     },
9804                     {
9805                         tag: 'ul',
9806                         cls: 'roo-select2-choices',
9807                         cn:[
9808                             {
9809                                 tag: 'li',
9810                                 cls: 'roo-select2-search-field',
9811                                 cn: [
9812
9813                                     inputblock
9814                                 ]
9815                             }
9816                         ]
9817                     }
9818                 ]
9819             }
9820         };
9821         
9822         var combobox = {
9823             cls: 'roo-select2-container input-group',
9824             cn: [
9825                 box
9826 //                {
9827 //                    tag: 'ul',
9828 //                    cls: 'typeahead typeahead-long dropdown-menu',
9829 //                    style: 'display:none'
9830 //                }
9831             ]
9832         };
9833         
9834         if(!this.multiple && this.showToggleBtn){
9835             
9836             var caret = {
9837                         tag: 'span',
9838                         cls: 'caret'
9839              };
9840             if (this.caret != false) {
9841                 caret = {
9842                      tag: 'i',
9843                      cls: 'fa fa-' + this.caret
9844                 };
9845                 
9846             }
9847             
9848             combobox.cn.push({
9849                 tag :'span',
9850                 cls : 'input-group-addon btn dropdown-toggle',
9851                 cn : [
9852                     caret,
9853                     {
9854                         tag: 'span',
9855                         cls: 'combobox-clear',
9856                         cn  : [
9857                             {
9858                                 tag : 'i',
9859                                 cls: 'icon-remove'
9860                             }
9861                         ]
9862                     }
9863                 ]
9864
9865             })
9866         }
9867         
9868         if(this.multiple){
9869             combobox.cls += ' roo-select2-container-multi';
9870         }
9871         
9872         if (align ==='left' && this.fieldLabel.length) {
9873             
9874             cfg.cls += ' roo-form-group-label-left';
9875
9876             cfg.cn = [
9877                 {
9878                     tag : 'i',
9879                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9880                     tooltip : 'This field is required'
9881                 },
9882                 {
9883                     tag: 'label',
9884                     'for' :  id,
9885                     cls : 'control-label',
9886                     html : this.fieldLabel
9887
9888                 },
9889                 {
9890                     cls : "", 
9891                     cn: [
9892                         combobox
9893                     ]
9894                 }
9895
9896             ];
9897             
9898             var labelCfg = cfg.cn[1];
9899             var contentCfg = cfg.cn[2];
9900             
9901             if(this.indicatorpos == 'right'){
9902                 cfg.cn = [
9903                     {
9904                         tag: 'label',
9905                         'for' :  id,
9906                         cls : 'control-label',
9907                         cn : [
9908                             {
9909                                 tag : 'span',
9910                                 html : this.fieldLabel
9911                             },
9912                             {
9913                                 tag : 'i',
9914                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9915                                 tooltip : 'This field is required'
9916                             }
9917                         ]
9918                     },
9919                     {
9920                         cls : "", 
9921                         cn: [
9922                             combobox
9923                         ]
9924                     }
9925
9926                 ];
9927                 
9928                 labelCfg = cfg.cn[0];
9929                 contentCfg = cfg.cn[1];
9930             }
9931             
9932             if(this.labelWidth > 12){
9933                 labelCfg.style = "width: " + this.labelWidth + 'px';
9934             }
9935             
9936             if(this.labelWidth < 13 && this.labelmd == 0){
9937                 this.labelmd = this.labelWidth;
9938             }
9939             
9940             if(this.labellg > 0){
9941                 labelCfg.cls += ' col-lg-' + this.labellg;
9942                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9943             }
9944             
9945             if(this.labelmd > 0){
9946                 labelCfg.cls += ' col-md-' + this.labelmd;
9947                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9948             }
9949             
9950             if(this.labelsm > 0){
9951                 labelCfg.cls += ' col-sm-' + this.labelsm;
9952                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9953             }
9954             
9955             if(this.labelxs > 0){
9956                 labelCfg.cls += ' col-xs-' + this.labelxs;
9957                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9958             }
9959             
9960         } else if ( this.fieldLabel.length) {
9961 //                Roo.log(" label");
9962             cfg.cn = [
9963                 {
9964                    tag : 'i',
9965                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9966                    tooltip : 'This field is required'
9967                },
9968                {
9969                    tag: 'label',
9970                    //cls : 'input-group-addon',
9971                    html : this.fieldLabel
9972
9973                },
9974
9975                combobox
9976
9977             ];
9978             
9979             if(this.indicatorpos == 'right'){
9980                 
9981                 cfg.cn = [
9982                     {
9983                        tag: 'label',
9984                        cn : [
9985                            {
9986                                tag : 'span',
9987                                html : this.fieldLabel
9988                            },
9989                            {
9990                               tag : 'i',
9991                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9992                               tooltip : 'This field is required'
9993                            }
9994                        ]
9995
9996                     },
9997                     combobox
9998
9999                 ];
10000
10001             }
10002
10003         } else {
10004             
10005 //                Roo.log(" no label && no align");
10006                 cfg = combobox
10007                      
10008                 
10009         }
10010         
10011         var settings=this;
10012         ['xs','sm','md','lg'].map(function(size){
10013             if (settings[size]) {
10014                 cfg.cls += ' col-' + size + '-' + settings[size];
10015             }
10016         });
10017         
10018         return cfg;
10019         
10020     },
10021     
10022     
10023     
10024     // private
10025     onResize : function(w, h){
10026 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10027 //        if(typeof w == 'number'){
10028 //            var x = w - this.trigger.getWidth();
10029 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10030 //            this.trigger.setStyle('left', x+'px');
10031 //        }
10032     },
10033
10034     // private
10035     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10036
10037     // private
10038     getResizeEl : function(){
10039         return this.inputEl();
10040     },
10041
10042     // private
10043     getPositionEl : function(){
10044         return this.inputEl();
10045     },
10046
10047     // private
10048     alignErrorIcon : function(){
10049         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10050     },
10051
10052     // private
10053     initEvents : function(){
10054         
10055         this.createList();
10056         
10057         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10058         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10059         if(!this.multiple && this.showToggleBtn){
10060             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10061             if(this.hideTrigger){
10062                 this.trigger.setDisplayed(false);
10063             }
10064             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10065         }
10066         
10067         if(this.multiple){
10068             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10069         }
10070         
10071         if(this.removable && !this.editable && !this.tickable){
10072             var close = this.closeTriggerEl();
10073             
10074             if(close){
10075                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10076                 close.on('click', this.removeBtnClick, this, close);
10077             }
10078         }
10079         
10080         //this.trigger.addClassOnOver('x-form-trigger-over');
10081         //this.trigger.addClassOnClick('x-form-trigger-click');
10082         
10083         //if(!this.width){
10084         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10085         //}
10086     },
10087     
10088     closeTriggerEl : function()
10089     {
10090         var close = this.el.select('.roo-combo-removable-btn', true).first();
10091         return close ? close : false;
10092     },
10093     
10094     removeBtnClick : function(e, h, el)
10095     {
10096         e.preventDefault();
10097         
10098         if(this.fireEvent("remove", this) !== false){
10099             this.reset();
10100             this.fireEvent("afterremove", this)
10101         }
10102     },
10103     
10104     createList : function()
10105     {
10106         this.list = Roo.get(document.body).createChild({
10107             tag: 'ul',
10108             cls: 'typeahead typeahead-long dropdown-menu',
10109             style: 'display:none'
10110         });
10111         
10112         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10113         
10114     },
10115
10116     // private
10117     initTrigger : function(){
10118        
10119     },
10120
10121     // private
10122     onDestroy : function(){
10123         if(this.trigger){
10124             this.trigger.removeAllListeners();
10125           //  this.trigger.remove();
10126         }
10127         //if(this.wrap){
10128         //    this.wrap.remove();
10129         //}
10130         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10131     },
10132
10133     // private
10134     onFocus : function(){
10135         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10136         /*
10137         if(!this.mimicing){
10138             this.wrap.addClass('x-trigger-wrap-focus');
10139             this.mimicing = true;
10140             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10141             if(this.monitorTab){
10142                 this.el.on("keydown", this.checkTab, this);
10143             }
10144         }
10145         */
10146     },
10147
10148     // private
10149     checkTab : function(e){
10150         if(e.getKey() == e.TAB){
10151             this.triggerBlur();
10152         }
10153     },
10154
10155     // private
10156     onBlur : function(){
10157         // do nothing
10158     },
10159
10160     // private
10161     mimicBlur : function(e, t){
10162         /*
10163         if(!this.wrap.contains(t) && this.validateBlur()){
10164             this.triggerBlur();
10165         }
10166         */
10167     },
10168
10169     // private
10170     triggerBlur : function(){
10171         this.mimicing = false;
10172         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10173         if(this.monitorTab){
10174             this.el.un("keydown", this.checkTab, this);
10175         }
10176         //this.wrap.removeClass('x-trigger-wrap-focus');
10177         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10178     },
10179
10180     // private
10181     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10182     validateBlur : function(e, t){
10183         return true;
10184     },
10185
10186     // private
10187     onDisable : function(){
10188         this.inputEl().dom.disabled = true;
10189         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10190         //if(this.wrap){
10191         //    this.wrap.addClass('x-item-disabled');
10192         //}
10193     },
10194
10195     // private
10196     onEnable : function(){
10197         this.inputEl().dom.disabled = false;
10198         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10199         //if(this.wrap){
10200         //    this.el.removeClass('x-item-disabled');
10201         //}
10202     },
10203
10204     // private
10205     onShow : function(){
10206         var ae = this.getActionEl();
10207         
10208         if(ae){
10209             ae.dom.style.display = '';
10210             ae.dom.style.visibility = 'visible';
10211         }
10212     },
10213
10214     // private
10215     
10216     onHide : function(){
10217         var ae = this.getActionEl();
10218         ae.dom.style.display = 'none';
10219     },
10220
10221     /**
10222      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10223      * by an implementing function.
10224      * @method
10225      * @param {EventObject} e
10226      */
10227     onTriggerClick : Roo.emptyFn
10228 });
10229  /*
10230  * Based on:
10231  * Ext JS Library 1.1.1
10232  * Copyright(c) 2006-2007, Ext JS, LLC.
10233  *
10234  * Originally Released Under LGPL - original licence link has changed is not relivant.
10235  *
10236  * Fork - LGPL
10237  * <script type="text/javascript">
10238  */
10239
10240
10241 /**
10242  * @class Roo.data.SortTypes
10243  * @singleton
10244  * Defines the default sorting (casting?) comparison functions used when sorting data.
10245  */
10246 Roo.data.SortTypes = {
10247     /**
10248      * Default sort that does nothing
10249      * @param {Mixed} s The value being converted
10250      * @return {Mixed} The comparison value
10251      */
10252     none : function(s){
10253         return s;
10254     },
10255     
10256     /**
10257      * The regular expression used to strip tags
10258      * @type {RegExp}
10259      * @property
10260      */
10261     stripTagsRE : /<\/?[^>]+>/gi,
10262     
10263     /**
10264      * Strips all HTML tags to sort on text only
10265      * @param {Mixed} s The value being converted
10266      * @return {String} The comparison value
10267      */
10268     asText : function(s){
10269         return String(s).replace(this.stripTagsRE, "");
10270     },
10271     
10272     /**
10273      * Strips all HTML tags to sort on text only - Case insensitive
10274      * @param {Mixed} s The value being converted
10275      * @return {String} The comparison value
10276      */
10277     asUCText : function(s){
10278         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10279     },
10280     
10281     /**
10282      * Case insensitive string
10283      * @param {Mixed} s The value being converted
10284      * @return {String} The comparison value
10285      */
10286     asUCString : function(s) {
10287         return String(s).toUpperCase();
10288     },
10289     
10290     /**
10291      * Date sorting
10292      * @param {Mixed} s The value being converted
10293      * @return {Number} The comparison value
10294      */
10295     asDate : function(s) {
10296         if(!s){
10297             return 0;
10298         }
10299         if(s instanceof Date){
10300             return s.getTime();
10301         }
10302         return Date.parse(String(s));
10303     },
10304     
10305     /**
10306      * Float sorting
10307      * @param {Mixed} s The value being converted
10308      * @return {Float} The comparison value
10309      */
10310     asFloat : function(s) {
10311         var val = parseFloat(String(s).replace(/,/g, ""));
10312         if(isNaN(val)) {
10313             val = 0;
10314         }
10315         return val;
10316     },
10317     
10318     /**
10319      * Integer sorting
10320      * @param {Mixed} s The value being converted
10321      * @return {Number} The comparison value
10322      */
10323     asInt : function(s) {
10324         var val = parseInt(String(s).replace(/,/g, ""));
10325         if(isNaN(val)) {
10326             val = 0;
10327         }
10328         return val;
10329     }
10330 };/*
10331  * Based on:
10332  * Ext JS Library 1.1.1
10333  * Copyright(c) 2006-2007, Ext JS, LLC.
10334  *
10335  * Originally Released Under LGPL - original licence link has changed is not relivant.
10336  *
10337  * Fork - LGPL
10338  * <script type="text/javascript">
10339  */
10340
10341 /**
10342 * @class Roo.data.Record
10343  * Instances of this class encapsulate both record <em>definition</em> information, and record
10344  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10345  * to access Records cached in an {@link Roo.data.Store} object.<br>
10346  * <p>
10347  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10348  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10349  * objects.<br>
10350  * <p>
10351  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10352  * @constructor
10353  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10354  * {@link #create}. The parameters are the same.
10355  * @param {Array} data An associative Array of data values keyed by the field name.
10356  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10357  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10358  * not specified an integer id is generated.
10359  */
10360 Roo.data.Record = function(data, id){
10361     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10362     this.data = data;
10363 };
10364
10365 /**
10366  * Generate a constructor for a specific record layout.
10367  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10368  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10369  * Each field definition object may contain the following properties: <ul>
10370  * <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,
10371  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10372  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10373  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10374  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10375  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10376  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10377  * this may be omitted.</p></li>
10378  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10379  * <ul><li>auto (Default, implies no conversion)</li>
10380  * <li>string</li>
10381  * <li>int</li>
10382  * <li>float</li>
10383  * <li>boolean</li>
10384  * <li>date</li></ul></p></li>
10385  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10386  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10387  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10388  * by the Reader into an object that will be stored in the Record. It is passed the
10389  * following parameters:<ul>
10390  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10391  * </ul></p></li>
10392  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10393  * </ul>
10394  * <br>usage:<br><pre><code>
10395 var TopicRecord = Roo.data.Record.create(
10396     {name: 'title', mapping: 'topic_title'},
10397     {name: 'author', mapping: 'username'},
10398     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10399     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10400     {name: 'lastPoster', mapping: 'user2'},
10401     {name: 'excerpt', mapping: 'post_text'}
10402 );
10403
10404 var myNewRecord = new TopicRecord({
10405     title: 'Do my job please',
10406     author: 'noobie',
10407     totalPosts: 1,
10408     lastPost: new Date(),
10409     lastPoster: 'Animal',
10410     excerpt: 'No way dude!'
10411 });
10412 myStore.add(myNewRecord);
10413 </code></pre>
10414  * @method create
10415  * @static
10416  */
10417 Roo.data.Record.create = function(o){
10418     var f = function(){
10419         f.superclass.constructor.apply(this, arguments);
10420     };
10421     Roo.extend(f, Roo.data.Record);
10422     var p = f.prototype;
10423     p.fields = new Roo.util.MixedCollection(false, function(field){
10424         return field.name;
10425     });
10426     for(var i = 0, len = o.length; i < len; i++){
10427         p.fields.add(new Roo.data.Field(o[i]));
10428     }
10429     f.getField = function(name){
10430         return p.fields.get(name);  
10431     };
10432     return f;
10433 };
10434
10435 Roo.data.Record.AUTO_ID = 1000;
10436 Roo.data.Record.EDIT = 'edit';
10437 Roo.data.Record.REJECT = 'reject';
10438 Roo.data.Record.COMMIT = 'commit';
10439
10440 Roo.data.Record.prototype = {
10441     /**
10442      * Readonly flag - true if this record has been modified.
10443      * @type Boolean
10444      */
10445     dirty : false,
10446     editing : false,
10447     error: null,
10448     modified: null,
10449
10450     // private
10451     join : function(store){
10452         this.store = store;
10453     },
10454
10455     /**
10456      * Set the named field to the specified value.
10457      * @param {String} name The name of the field to set.
10458      * @param {Object} value The value to set the field to.
10459      */
10460     set : function(name, value){
10461         if(this.data[name] == value){
10462             return;
10463         }
10464         this.dirty = true;
10465         if(!this.modified){
10466             this.modified = {};
10467         }
10468         if(typeof this.modified[name] == 'undefined'){
10469             this.modified[name] = this.data[name];
10470         }
10471         this.data[name] = value;
10472         if(!this.editing && this.store){
10473             this.store.afterEdit(this);
10474         }       
10475     },
10476
10477     /**
10478      * Get the value of the named field.
10479      * @param {String} name The name of the field to get the value of.
10480      * @return {Object} The value of the field.
10481      */
10482     get : function(name){
10483         return this.data[name]; 
10484     },
10485
10486     // private
10487     beginEdit : function(){
10488         this.editing = true;
10489         this.modified = {}; 
10490     },
10491
10492     // private
10493     cancelEdit : function(){
10494         this.editing = false;
10495         delete this.modified;
10496     },
10497
10498     // private
10499     endEdit : function(){
10500         this.editing = false;
10501         if(this.dirty && this.store){
10502             this.store.afterEdit(this);
10503         }
10504     },
10505
10506     /**
10507      * Usually called by the {@link Roo.data.Store} which owns the Record.
10508      * Rejects all changes made to the Record since either creation, or the last commit operation.
10509      * Modified fields are reverted to their original values.
10510      * <p>
10511      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10512      * of reject operations.
10513      */
10514     reject : function(){
10515         var m = this.modified;
10516         for(var n in m){
10517             if(typeof m[n] != "function"){
10518                 this.data[n] = m[n];
10519             }
10520         }
10521         this.dirty = false;
10522         delete this.modified;
10523         this.editing = false;
10524         if(this.store){
10525             this.store.afterReject(this);
10526         }
10527     },
10528
10529     /**
10530      * Usually called by the {@link Roo.data.Store} which owns the Record.
10531      * Commits all changes made to the Record since either creation, or the last commit operation.
10532      * <p>
10533      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10534      * of commit operations.
10535      */
10536     commit : function(){
10537         this.dirty = false;
10538         delete this.modified;
10539         this.editing = false;
10540         if(this.store){
10541             this.store.afterCommit(this);
10542         }
10543     },
10544
10545     // private
10546     hasError : function(){
10547         return this.error != null;
10548     },
10549
10550     // private
10551     clearError : function(){
10552         this.error = null;
10553     },
10554
10555     /**
10556      * Creates a copy of this record.
10557      * @param {String} id (optional) A new record id if you don't want to use this record's id
10558      * @return {Record}
10559      */
10560     copy : function(newId) {
10561         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10562     }
10563 };/*
10564  * Based on:
10565  * Ext JS Library 1.1.1
10566  * Copyright(c) 2006-2007, Ext JS, LLC.
10567  *
10568  * Originally Released Under LGPL - original licence link has changed is not relivant.
10569  *
10570  * Fork - LGPL
10571  * <script type="text/javascript">
10572  */
10573
10574
10575
10576 /**
10577  * @class Roo.data.Store
10578  * @extends Roo.util.Observable
10579  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10580  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10581  * <p>
10582  * 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
10583  * has no knowledge of the format of the data returned by the Proxy.<br>
10584  * <p>
10585  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10586  * instances from the data object. These records are cached and made available through accessor functions.
10587  * @constructor
10588  * Creates a new Store.
10589  * @param {Object} config A config object containing the objects needed for the Store to access data,
10590  * and read the data into Records.
10591  */
10592 Roo.data.Store = function(config){
10593     this.data = new Roo.util.MixedCollection(false);
10594     this.data.getKey = function(o){
10595         return o.id;
10596     };
10597     this.baseParams = {};
10598     // private
10599     this.paramNames = {
10600         "start" : "start",
10601         "limit" : "limit",
10602         "sort" : "sort",
10603         "dir" : "dir",
10604         "multisort" : "_multisort"
10605     };
10606
10607     if(config && config.data){
10608         this.inlineData = config.data;
10609         delete config.data;
10610     }
10611
10612     Roo.apply(this, config);
10613     
10614     if(this.reader){ // reader passed
10615         this.reader = Roo.factory(this.reader, Roo.data);
10616         this.reader.xmodule = this.xmodule || false;
10617         if(!this.recordType){
10618             this.recordType = this.reader.recordType;
10619         }
10620         if(this.reader.onMetaChange){
10621             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10622         }
10623     }
10624
10625     if(this.recordType){
10626         this.fields = this.recordType.prototype.fields;
10627     }
10628     this.modified = [];
10629
10630     this.addEvents({
10631         /**
10632          * @event datachanged
10633          * Fires when the data cache has changed, and a widget which is using this Store
10634          * as a Record cache should refresh its view.
10635          * @param {Store} this
10636          */
10637         datachanged : true,
10638         /**
10639          * @event metachange
10640          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10641          * @param {Store} this
10642          * @param {Object} meta The JSON metadata
10643          */
10644         metachange : true,
10645         /**
10646          * @event add
10647          * Fires when Records have been added to the Store
10648          * @param {Store} this
10649          * @param {Roo.data.Record[]} records The array of Records added
10650          * @param {Number} index The index at which the record(s) were added
10651          */
10652         add : true,
10653         /**
10654          * @event remove
10655          * Fires when a Record has been removed from the Store
10656          * @param {Store} this
10657          * @param {Roo.data.Record} record The Record that was removed
10658          * @param {Number} index The index at which the record was removed
10659          */
10660         remove : true,
10661         /**
10662          * @event update
10663          * Fires when a Record has been updated
10664          * @param {Store} this
10665          * @param {Roo.data.Record} record The Record that was updated
10666          * @param {String} operation The update operation being performed.  Value may be one of:
10667          * <pre><code>
10668  Roo.data.Record.EDIT
10669  Roo.data.Record.REJECT
10670  Roo.data.Record.COMMIT
10671          * </code></pre>
10672          */
10673         update : true,
10674         /**
10675          * @event clear
10676          * Fires when the data cache has been cleared.
10677          * @param {Store} this
10678          */
10679         clear : true,
10680         /**
10681          * @event beforeload
10682          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10683          * the load action will be canceled.
10684          * @param {Store} this
10685          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10686          */
10687         beforeload : true,
10688         /**
10689          * @event beforeloadadd
10690          * Fires after a new set of Records has been loaded.
10691          * @param {Store} this
10692          * @param {Roo.data.Record[]} records The Records that were loaded
10693          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10694          */
10695         beforeloadadd : true,
10696         /**
10697          * @event load
10698          * Fires after a new set of Records has been loaded, before they are added to the store.
10699          * @param {Store} this
10700          * @param {Roo.data.Record[]} records The Records that were loaded
10701          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10702          * @params {Object} return from reader
10703          */
10704         load : true,
10705         /**
10706          * @event loadexception
10707          * Fires if an exception occurs in the Proxy during loading.
10708          * Called with the signature of the Proxy's "loadexception" event.
10709          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10710          * 
10711          * @param {Proxy} 
10712          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10713          * @param {Object} load options 
10714          * @param {Object} jsonData from your request (normally this contains the Exception)
10715          */
10716         loadexception : true
10717     });
10718     
10719     if(this.proxy){
10720         this.proxy = Roo.factory(this.proxy, Roo.data);
10721         this.proxy.xmodule = this.xmodule || false;
10722         this.relayEvents(this.proxy,  ["loadexception"]);
10723     }
10724     this.sortToggle = {};
10725     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10726
10727     Roo.data.Store.superclass.constructor.call(this);
10728
10729     if(this.inlineData){
10730         this.loadData(this.inlineData);
10731         delete this.inlineData;
10732     }
10733 };
10734
10735 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10736      /**
10737     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10738     * without a remote query - used by combo/forms at present.
10739     */
10740     
10741     /**
10742     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10743     */
10744     /**
10745     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10746     */
10747     /**
10748     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10749     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10750     */
10751     /**
10752     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10753     * on any HTTP request
10754     */
10755     /**
10756     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10757     */
10758     /**
10759     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10760     */
10761     multiSort: false,
10762     /**
10763     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10764     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10765     */
10766     remoteSort : false,
10767
10768     /**
10769     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10770      * loaded or when a record is removed. (defaults to false).
10771     */
10772     pruneModifiedRecords : false,
10773
10774     // private
10775     lastOptions : null,
10776
10777     /**
10778      * Add Records to the Store and fires the add event.
10779      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10780      */
10781     add : function(records){
10782         records = [].concat(records);
10783         for(var i = 0, len = records.length; i < len; i++){
10784             records[i].join(this);
10785         }
10786         var index = this.data.length;
10787         this.data.addAll(records);
10788         this.fireEvent("add", this, records, index);
10789     },
10790
10791     /**
10792      * Remove a Record from the Store and fires the remove event.
10793      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10794      */
10795     remove : function(record){
10796         var index = this.data.indexOf(record);
10797         this.data.removeAt(index);
10798         if(this.pruneModifiedRecords){
10799             this.modified.remove(record);
10800         }
10801         this.fireEvent("remove", this, record, index);
10802     },
10803
10804     /**
10805      * Remove all Records from the Store and fires the clear event.
10806      */
10807     removeAll : function(){
10808         this.data.clear();
10809         if(this.pruneModifiedRecords){
10810             this.modified = [];
10811         }
10812         this.fireEvent("clear", this);
10813     },
10814
10815     /**
10816      * Inserts Records to the Store at the given index and fires the add event.
10817      * @param {Number} index The start index at which to insert the passed Records.
10818      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10819      */
10820     insert : function(index, records){
10821         records = [].concat(records);
10822         for(var i = 0, len = records.length; i < len; i++){
10823             this.data.insert(index, records[i]);
10824             records[i].join(this);
10825         }
10826         this.fireEvent("add", this, records, index);
10827     },
10828
10829     /**
10830      * Get the index within the cache of the passed Record.
10831      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10832      * @return {Number} The index of the passed Record. Returns -1 if not found.
10833      */
10834     indexOf : function(record){
10835         return this.data.indexOf(record);
10836     },
10837
10838     /**
10839      * Get the index within the cache of the Record with the passed id.
10840      * @param {String} id The id of the Record to find.
10841      * @return {Number} The index of the Record. Returns -1 if not found.
10842      */
10843     indexOfId : function(id){
10844         return this.data.indexOfKey(id);
10845     },
10846
10847     /**
10848      * Get the Record with the specified id.
10849      * @param {String} id The id of the Record to find.
10850      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10851      */
10852     getById : function(id){
10853         return this.data.key(id);
10854     },
10855
10856     /**
10857      * Get the Record at the specified index.
10858      * @param {Number} index The index of the Record to find.
10859      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10860      */
10861     getAt : function(index){
10862         return this.data.itemAt(index);
10863     },
10864
10865     /**
10866      * Returns a range of Records between specified indices.
10867      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10868      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10869      * @return {Roo.data.Record[]} An array of Records
10870      */
10871     getRange : function(start, end){
10872         return this.data.getRange(start, end);
10873     },
10874
10875     // private
10876     storeOptions : function(o){
10877         o = Roo.apply({}, o);
10878         delete o.callback;
10879         delete o.scope;
10880         this.lastOptions = o;
10881     },
10882
10883     /**
10884      * Loads the Record cache from the configured Proxy using the configured Reader.
10885      * <p>
10886      * If using remote paging, then the first load call must specify the <em>start</em>
10887      * and <em>limit</em> properties in the options.params property to establish the initial
10888      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10889      * <p>
10890      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10891      * and this call will return before the new data has been loaded. Perform any post-processing
10892      * in a callback function, or in a "load" event handler.</strong>
10893      * <p>
10894      * @param {Object} options An object containing properties which control loading options:<ul>
10895      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10896      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10897      * passed the following arguments:<ul>
10898      * <li>r : Roo.data.Record[]</li>
10899      * <li>options: Options object from the load call</li>
10900      * <li>success: Boolean success indicator</li></ul></li>
10901      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10902      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10903      * </ul>
10904      */
10905     load : function(options){
10906         options = options || {};
10907         if(this.fireEvent("beforeload", this, options) !== false){
10908             this.storeOptions(options);
10909             var p = Roo.apply(options.params || {}, this.baseParams);
10910             // if meta was not loaded from remote source.. try requesting it.
10911             if (!this.reader.metaFromRemote) {
10912                 p._requestMeta = 1;
10913             }
10914             if(this.sortInfo && this.remoteSort){
10915                 var pn = this.paramNames;
10916                 p[pn["sort"]] = this.sortInfo.field;
10917                 p[pn["dir"]] = this.sortInfo.direction;
10918             }
10919             if (this.multiSort) {
10920                 var pn = this.paramNames;
10921                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10922             }
10923             
10924             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10925         }
10926     },
10927
10928     /**
10929      * Reloads the Record cache from the configured Proxy using the configured Reader and
10930      * the options from the last load operation performed.
10931      * @param {Object} options (optional) An object containing properties which may override the options
10932      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10933      * the most recently used options are reused).
10934      */
10935     reload : function(options){
10936         this.load(Roo.applyIf(options||{}, this.lastOptions));
10937     },
10938
10939     // private
10940     // Called as a callback by the Reader during a load operation.
10941     loadRecords : function(o, options, success){
10942         if(!o || success === false){
10943             if(success !== false){
10944                 this.fireEvent("load", this, [], options, o);
10945             }
10946             if(options.callback){
10947                 options.callback.call(options.scope || this, [], options, false);
10948             }
10949             return;
10950         }
10951         // if data returned failure - throw an exception.
10952         if (o.success === false) {
10953             // show a message if no listener is registered.
10954             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10955                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10956             }
10957             // loadmask wil be hooked into this..
10958             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10959             return;
10960         }
10961         var r = o.records, t = o.totalRecords || r.length;
10962         
10963         this.fireEvent("beforeloadadd", this, r, options, o);
10964         
10965         if(!options || options.add !== true){
10966             if(this.pruneModifiedRecords){
10967                 this.modified = [];
10968             }
10969             for(var i = 0, len = r.length; i < len; i++){
10970                 r[i].join(this);
10971             }
10972             if(this.snapshot){
10973                 this.data = this.snapshot;
10974                 delete this.snapshot;
10975             }
10976             this.data.clear();
10977             this.data.addAll(r);
10978             this.totalLength = t;
10979             this.applySort();
10980             this.fireEvent("datachanged", this);
10981         }else{
10982             this.totalLength = Math.max(t, this.data.length+r.length);
10983             this.add(r);
10984         }
10985         this.fireEvent("load", this, r, options, o);
10986         if(options.callback){
10987             options.callback.call(options.scope || this, r, options, true);
10988         }
10989     },
10990
10991
10992     /**
10993      * Loads data from a passed data block. A Reader which understands the format of the data
10994      * must have been configured in the constructor.
10995      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10996      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10997      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10998      */
10999     loadData : function(o, append){
11000         var r = this.reader.readRecords(o);
11001         this.loadRecords(r, {add: append}, true);
11002     },
11003
11004     /**
11005      * Gets the number of cached records.
11006      * <p>
11007      * <em>If using paging, this may not be the total size of the dataset. If the data object
11008      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11009      * the data set size</em>
11010      */
11011     getCount : function(){
11012         return this.data.length || 0;
11013     },
11014
11015     /**
11016      * Gets the total number of records in the dataset as returned by the server.
11017      * <p>
11018      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11019      * the dataset size</em>
11020      */
11021     getTotalCount : function(){
11022         return this.totalLength || 0;
11023     },
11024
11025     /**
11026      * Returns the sort state of the Store as an object with two properties:
11027      * <pre><code>
11028  field {String} The name of the field by which the Records are sorted
11029  direction {String} The sort order, "ASC" or "DESC"
11030      * </code></pre>
11031      */
11032     getSortState : function(){
11033         return this.sortInfo;
11034     },
11035
11036     // private
11037     applySort : function(){
11038         if(this.sortInfo && !this.remoteSort){
11039             var s = this.sortInfo, f = s.field;
11040             var st = this.fields.get(f).sortType;
11041             var fn = function(r1, r2){
11042                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11043                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11044             };
11045             this.data.sort(s.direction, fn);
11046             if(this.snapshot && this.snapshot != this.data){
11047                 this.snapshot.sort(s.direction, fn);
11048             }
11049         }
11050     },
11051
11052     /**
11053      * Sets the default sort column and order to be used by the next load operation.
11054      * @param {String} fieldName The name of the field to sort by.
11055      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11056      */
11057     setDefaultSort : function(field, dir){
11058         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11059     },
11060
11061     /**
11062      * Sort the Records.
11063      * If remote sorting is used, the sort is performed on the server, and the cache is
11064      * reloaded. If local sorting is used, the cache is sorted internally.
11065      * @param {String} fieldName The name of the field to sort by.
11066      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11067      */
11068     sort : function(fieldName, dir){
11069         var f = this.fields.get(fieldName);
11070         if(!dir){
11071             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11072             
11073             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11074                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11075             }else{
11076                 dir = f.sortDir;
11077             }
11078         }
11079         this.sortToggle[f.name] = dir;
11080         this.sortInfo = {field: f.name, direction: dir};
11081         if(!this.remoteSort){
11082             this.applySort();
11083             this.fireEvent("datachanged", this);
11084         }else{
11085             this.load(this.lastOptions);
11086         }
11087     },
11088
11089     /**
11090      * Calls the specified function for each of the Records in the cache.
11091      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11092      * Returning <em>false</em> aborts and exits the iteration.
11093      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11094      */
11095     each : function(fn, scope){
11096         this.data.each(fn, scope);
11097     },
11098
11099     /**
11100      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11101      * (e.g., during paging).
11102      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11103      */
11104     getModifiedRecords : function(){
11105         return this.modified;
11106     },
11107
11108     // private
11109     createFilterFn : function(property, value, anyMatch){
11110         if(!value.exec){ // not a regex
11111             value = String(value);
11112             if(value.length == 0){
11113                 return false;
11114             }
11115             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11116         }
11117         return function(r){
11118             return value.test(r.data[property]);
11119         };
11120     },
11121
11122     /**
11123      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11124      * @param {String} property A field on your records
11125      * @param {Number} start The record index to start at (defaults to 0)
11126      * @param {Number} end The last record index to include (defaults to length - 1)
11127      * @return {Number} The sum
11128      */
11129     sum : function(property, start, end){
11130         var rs = this.data.items, v = 0;
11131         start = start || 0;
11132         end = (end || end === 0) ? end : rs.length-1;
11133
11134         for(var i = start; i <= end; i++){
11135             v += (rs[i].data[property] || 0);
11136         }
11137         return v;
11138     },
11139
11140     /**
11141      * Filter the records by a specified property.
11142      * @param {String} field A field on your records
11143      * @param {String/RegExp} value Either a string that the field
11144      * should start with or a RegExp to test against the field
11145      * @param {Boolean} anyMatch True to match any part not just the beginning
11146      */
11147     filter : function(property, value, anyMatch){
11148         var fn = this.createFilterFn(property, value, anyMatch);
11149         return fn ? this.filterBy(fn) : this.clearFilter();
11150     },
11151
11152     /**
11153      * Filter by a function. The specified function will be called with each
11154      * record in this data source. If the function returns true the record is included,
11155      * otherwise it is filtered.
11156      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11157      * @param {Object} scope (optional) The scope of the function (defaults to this)
11158      */
11159     filterBy : function(fn, scope){
11160         this.snapshot = this.snapshot || this.data;
11161         this.data = this.queryBy(fn, scope||this);
11162         this.fireEvent("datachanged", this);
11163     },
11164
11165     /**
11166      * Query the records by a specified property.
11167      * @param {String} field A field on your records
11168      * @param {String/RegExp} value Either a string that the field
11169      * should start with or a RegExp to test against the field
11170      * @param {Boolean} anyMatch True to match any part not just the beginning
11171      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11172      */
11173     query : function(property, value, anyMatch){
11174         var fn = this.createFilterFn(property, value, anyMatch);
11175         return fn ? this.queryBy(fn) : this.data.clone();
11176     },
11177
11178     /**
11179      * Query by a function. The specified function will be called with each
11180      * record in this data source. If the function returns true the record is included
11181      * in the results.
11182      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11183      * @param {Object} scope (optional) The scope of the function (defaults to this)
11184       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11185      **/
11186     queryBy : function(fn, scope){
11187         var data = this.snapshot || this.data;
11188         return data.filterBy(fn, scope||this);
11189     },
11190
11191     /**
11192      * Collects unique values for a particular dataIndex from this store.
11193      * @param {String} dataIndex The property to collect
11194      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11195      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11196      * @return {Array} An array of the unique values
11197      **/
11198     collect : function(dataIndex, allowNull, bypassFilter){
11199         var d = (bypassFilter === true && this.snapshot) ?
11200                 this.snapshot.items : this.data.items;
11201         var v, sv, r = [], l = {};
11202         for(var i = 0, len = d.length; i < len; i++){
11203             v = d[i].data[dataIndex];
11204             sv = String(v);
11205             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11206                 l[sv] = true;
11207                 r[r.length] = v;
11208             }
11209         }
11210         return r;
11211     },
11212
11213     /**
11214      * Revert to a view of the Record cache with no filtering applied.
11215      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11216      */
11217     clearFilter : function(suppressEvent){
11218         if(this.snapshot && this.snapshot != this.data){
11219             this.data = this.snapshot;
11220             delete this.snapshot;
11221             if(suppressEvent !== true){
11222                 this.fireEvent("datachanged", this);
11223             }
11224         }
11225     },
11226
11227     // private
11228     afterEdit : function(record){
11229         if(this.modified.indexOf(record) == -1){
11230             this.modified.push(record);
11231         }
11232         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11233     },
11234     
11235     // private
11236     afterReject : function(record){
11237         this.modified.remove(record);
11238         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11239     },
11240
11241     // private
11242     afterCommit : function(record){
11243         this.modified.remove(record);
11244         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11245     },
11246
11247     /**
11248      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11249      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11250      */
11251     commitChanges : function(){
11252         var m = this.modified.slice(0);
11253         this.modified = [];
11254         for(var i = 0, len = m.length; i < len; i++){
11255             m[i].commit();
11256         }
11257     },
11258
11259     /**
11260      * Cancel outstanding changes on all changed records.
11261      */
11262     rejectChanges : function(){
11263         var m = this.modified.slice(0);
11264         this.modified = [];
11265         for(var i = 0, len = m.length; i < len; i++){
11266             m[i].reject();
11267         }
11268     },
11269
11270     onMetaChange : function(meta, rtype, o){
11271         this.recordType = rtype;
11272         this.fields = rtype.prototype.fields;
11273         delete this.snapshot;
11274         this.sortInfo = meta.sortInfo || this.sortInfo;
11275         this.modified = [];
11276         this.fireEvent('metachange', this, this.reader.meta);
11277     },
11278     
11279     moveIndex : function(data, type)
11280     {
11281         var index = this.indexOf(data);
11282         
11283         var newIndex = index + type;
11284         
11285         this.remove(data);
11286         
11287         this.insert(newIndex, data);
11288         
11289     }
11290 });/*
11291  * Based on:
11292  * Ext JS Library 1.1.1
11293  * Copyright(c) 2006-2007, Ext JS, LLC.
11294  *
11295  * Originally Released Under LGPL - original licence link has changed is not relivant.
11296  *
11297  * Fork - LGPL
11298  * <script type="text/javascript">
11299  */
11300
11301 /**
11302  * @class Roo.data.SimpleStore
11303  * @extends Roo.data.Store
11304  * Small helper class to make creating Stores from Array data easier.
11305  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11306  * @cfg {Array} fields An array of field definition objects, or field name strings.
11307  * @cfg {Array} data The multi-dimensional array of data
11308  * @constructor
11309  * @param {Object} config
11310  */
11311 Roo.data.SimpleStore = function(config){
11312     Roo.data.SimpleStore.superclass.constructor.call(this, {
11313         isLocal : true,
11314         reader: new Roo.data.ArrayReader({
11315                 id: config.id
11316             },
11317             Roo.data.Record.create(config.fields)
11318         ),
11319         proxy : new Roo.data.MemoryProxy(config.data)
11320     });
11321     this.load();
11322 };
11323 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11324  * Based on:
11325  * Ext JS Library 1.1.1
11326  * Copyright(c) 2006-2007, Ext JS, LLC.
11327  *
11328  * Originally Released Under LGPL - original licence link has changed is not relivant.
11329  *
11330  * Fork - LGPL
11331  * <script type="text/javascript">
11332  */
11333
11334 /**
11335 /**
11336  * @extends Roo.data.Store
11337  * @class Roo.data.JsonStore
11338  * Small helper class to make creating Stores for JSON data easier. <br/>
11339 <pre><code>
11340 var store = new Roo.data.JsonStore({
11341     url: 'get-images.php',
11342     root: 'images',
11343     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11344 });
11345 </code></pre>
11346  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11347  * JsonReader and HttpProxy (unless inline data is provided).</b>
11348  * @cfg {Array} fields An array of field definition objects, or field name strings.
11349  * @constructor
11350  * @param {Object} config
11351  */
11352 Roo.data.JsonStore = function(c){
11353     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11354         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11355         reader: new Roo.data.JsonReader(c, c.fields)
11356     }));
11357 };
11358 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11359  * Based on:
11360  * Ext JS Library 1.1.1
11361  * Copyright(c) 2006-2007, Ext JS, LLC.
11362  *
11363  * Originally Released Under LGPL - original licence link has changed is not relivant.
11364  *
11365  * Fork - LGPL
11366  * <script type="text/javascript">
11367  */
11368
11369  
11370 Roo.data.Field = function(config){
11371     if(typeof config == "string"){
11372         config = {name: config};
11373     }
11374     Roo.apply(this, config);
11375     
11376     if(!this.type){
11377         this.type = "auto";
11378     }
11379     
11380     var st = Roo.data.SortTypes;
11381     // named sortTypes are supported, here we look them up
11382     if(typeof this.sortType == "string"){
11383         this.sortType = st[this.sortType];
11384     }
11385     
11386     // set default sortType for strings and dates
11387     if(!this.sortType){
11388         switch(this.type){
11389             case "string":
11390                 this.sortType = st.asUCString;
11391                 break;
11392             case "date":
11393                 this.sortType = st.asDate;
11394                 break;
11395             default:
11396                 this.sortType = st.none;
11397         }
11398     }
11399
11400     // define once
11401     var stripRe = /[\$,%]/g;
11402
11403     // prebuilt conversion function for this field, instead of
11404     // switching every time we're reading a value
11405     if(!this.convert){
11406         var cv, dateFormat = this.dateFormat;
11407         switch(this.type){
11408             case "":
11409             case "auto":
11410             case undefined:
11411                 cv = function(v){ return v; };
11412                 break;
11413             case "string":
11414                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11415                 break;
11416             case "int":
11417                 cv = function(v){
11418                     return v !== undefined && v !== null && v !== '' ?
11419                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11420                     };
11421                 break;
11422             case "float":
11423                 cv = function(v){
11424                     return v !== undefined && v !== null && v !== '' ?
11425                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11426                     };
11427                 break;
11428             case "bool":
11429             case "boolean":
11430                 cv = function(v){ return v === true || v === "true" || v == 1; };
11431                 break;
11432             case "date":
11433                 cv = function(v){
11434                     if(!v){
11435                         return '';
11436                     }
11437                     if(v instanceof Date){
11438                         return v;
11439                     }
11440                     if(dateFormat){
11441                         if(dateFormat == "timestamp"){
11442                             return new Date(v*1000);
11443                         }
11444                         return Date.parseDate(v, dateFormat);
11445                     }
11446                     var parsed = Date.parse(v);
11447                     return parsed ? new Date(parsed) : null;
11448                 };
11449              break;
11450             
11451         }
11452         this.convert = cv;
11453     }
11454 };
11455
11456 Roo.data.Field.prototype = {
11457     dateFormat: null,
11458     defaultValue: "",
11459     mapping: null,
11460     sortType : null,
11461     sortDir : "ASC"
11462 };/*
11463  * Based on:
11464  * Ext JS Library 1.1.1
11465  * Copyright(c) 2006-2007, Ext JS, LLC.
11466  *
11467  * Originally Released Under LGPL - original licence link has changed is not relivant.
11468  *
11469  * Fork - LGPL
11470  * <script type="text/javascript">
11471  */
11472  
11473 // Base class for reading structured data from a data source.  This class is intended to be
11474 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11475
11476 /**
11477  * @class Roo.data.DataReader
11478  * Base class for reading structured data from a data source.  This class is intended to be
11479  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11480  */
11481
11482 Roo.data.DataReader = function(meta, recordType){
11483     
11484     this.meta = meta;
11485     
11486     this.recordType = recordType instanceof Array ? 
11487         Roo.data.Record.create(recordType) : recordType;
11488 };
11489
11490 Roo.data.DataReader.prototype = {
11491      /**
11492      * Create an empty record
11493      * @param {Object} data (optional) - overlay some values
11494      * @return {Roo.data.Record} record created.
11495      */
11496     newRow :  function(d) {
11497         var da =  {};
11498         this.recordType.prototype.fields.each(function(c) {
11499             switch( c.type) {
11500                 case 'int' : da[c.name] = 0; break;
11501                 case 'date' : da[c.name] = new Date(); break;
11502                 case 'float' : da[c.name] = 0.0; break;
11503                 case 'boolean' : da[c.name] = false; break;
11504                 default : da[c.name] = ""; break;
11505             }
11506             
11507         });
11508         return new this.recordType(Roo.apply(da, d));
11509     }
11510     
11511 };/*
11512  * Based on:
11513  * Ext JS Library 1.1.1
11514  * Copyright(c) 2006-2007, Ext JS, LLC.
11515  *
11516  * Originally Released Under LGPL - original licence link has changed is not relivant.
11517  *
11518  * Fork - LGPL
11519  * <script type="text/javascript">
11520  */
11521
11522 /**
11523  * @class Roo.data.DataProxy
11524  * @extends Roo.data.Observable
11525  * This class is an abstract base class for implementations which provide retrieval of
11526  * unformatted data objects.<br>
11527  * <p>
11528  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11529  * (of the appropriate type which knows how to parse the data object) to provide a block of
11530  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11531  * <p>
11532  * Custom implementations must implement the load method as described in
11533  * {@link Roo.data.HttpProxy#load}.
11534  */
11535 Roo.data.DataProxy = function(){
11536     this.addEvents({
11537         /**
11538          * @event beforeload
11539          * Fires before a network request is made to retrieve a data object.
11540          * @param {Object} This DataProxy object.
11541          * @param {Object} params The params parameter to the load function.
11542          */
11543         beforeload : true,
11544         /**
11545          * @event load
11546          * Fires before the load method's callback is called.
11547          * @param {Object} This DataProxy object.
11548          * @param {Object} o The data object.
11549          * @param {Object} arg The callback argument object passed to the load function.
11550          */
11551         load : true,
11552         /**
11553          * @event loadexception
11554          * Fires if an Exception occurs during data retrieval.
11555          * @param {Object} This DataProxy object.
11556          * @param {Object} o The data object.
11557          * @param {Object} arg The callback argument object passed to the load function.
11558          * @param {Object} e The Exception.
11559          */
11560         loadexception : true
11561     });
11562     Roo.data.DataProxy.superclass.constructor.call(this);
11563 };
11564
11565 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11566
11567     /**
11568      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11569      */
11570 /*
11571  * Based on:
11572  * Ext JS Library 1.1.1
11573  * Copyright(c) 2006-2007, Ext JS, LLC.
11574  *
11575  * Originally Released Under LGPL - original licence link has changed is not relivant.
11576  *
11577  * Fork - LGPL
11578  * <script type="text/javascript">
11579  */
11580 /**
11581  * @class Roo.data.MemoryProxy
11582  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11583  * to the Reader when its load method is called.
11584  * @constructor
11585  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11586  */
11587 Roo.data.MemoryProxy = function(data){
11588     if (data.data) {
11589         data = data.data;
11590     }
11591     Roo.data.MemoryProxy.superclass.constructor.call(this);
11592     this.data = data;
11593 };
11594
11595 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11596     
11597     /**
11598      * Load data from the requested source (in this case an in-memory
11599      * data object passed to the constructor), read the data object into
11600      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11601      * process that block using the passed callback.
11602      * @param {Object} params This parameter is not used by the MemoryProxy class.
11603      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11604      * object into a block of Roo.data.Records.
11605      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11606      * The function must be passed <ul>
11607      * <li>The Record block object</li>
11608      * <li>The "arg" argument from the load function</li>
11609      * <li>A boolean success indicator</li>
11610      * </ul>
11611      * @param {Object} scope The scope in which to call the callback
11612      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11613      */
11614     load : function(params, reader, callback, scope, arg){
11615         params = params || {};
11616         var result;
11617         try {
11618             result = reader.readRecords(this.data);
11619         }catch(e){
11620             this.fireEvent("loadexception", this, arg, null, e);
11621             callback.call(scope, null, arg, false);
11622             return;
11623         }
11624         callback.call(scope, result, arg, true);
11625     },
11626     
11627     // private
11628     update : function(params, records){
11629         
11630     }
11631 });/*
11632  * Based on:
11633  * Ext JS Library 1.1.1
11634  * Copyright(c) 2006-2007, Ext JS, LLC.
11635  *
11636  * Originally Released Under LGPL - original licence link has changed is not relivant.
11637  *
11638  * Fork - LGPL
11639  * <script type="text/javascript">
11640  */
11641 /**
11642  * @class Roo.data.HttpProxy
11643  * @extends Roo.data.DataProxy
11644  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11645  * configured to reference a certain URL.<br><br>
11646  * <p>
11647  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11648  * from which the running page was served.<br><br>
11649  * <p>
11650  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11651  * <p>
11652  * Be aware that to enable the browser to parse an XML document, the server must set
11653  * the Content-Type header in the HTTP response to "text/xml".
11654  * @constructor
11655  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11656  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11657  * will be used to make the request.
11658  */
11659 Roo.data.HttpProxy = function(conn){
11660     Roo.data.HttpProxy.superclass.constructor.call(this);
11661     // is conn a conn config or a real conn?
11662     this.conn = conn;
11663     this.useAjax = !conn || !conn.events;
11664   
11665 };
11666
11667 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11668     // thse are take from connection...
11669     
11670     /**
11671      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11672      */
11673     /**
11674      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11675      * extra parameters to each request made by this object. (defaults to undefined)
11676      */
11677     /**
11678      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11679      *  to each request made by this object. (defaults to undefined)
11680      */
11681     /**
11682      * @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)
11683      */
11684     /**
11685      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11686      */
11687      /**
11688      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11689      * @type Boolean
11690      */
11691   
11692
11693     /**
11694      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11695      * @type Boolean
11696      */
11697     /**
11698      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11699      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11700      * a finer-grained basis than the DataProxy events.
11701      */
11702     getConnection : function(){
11703         return this.useAjax ? Roo.Ajax : this.conn;
11704     },
11705
11706     /**
11707      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11708      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11709      * process that block using the passed callback.
11710      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11711      * for the request to the remote server.
11712      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11713      * object into a block of Roo.data.Records.
11714      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11715      * The function must be passed <ul>
11716      * <li>The Record block object</li>
11717      * <li>The "arg" argument from the load function</li>
11718      * <li>A boolean success indicator</li>
11719      * </ul>
11720      * @param {Object} scope The scope in which to call the callback
11721      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11722      */
11723     load : function(params, reader, callback, scope, arg){
11724         if(this.fireEvent("beforeload", this, params) !== false){
11725             var  o = {
11726                 params : params || {},
11727                 request: {
11728                     callback : callback,
11729                     scope : scope,
11730                     arg : arg
11731                 },
11732                 reader: reader,
11733                 callback : this.loadResponse,
11734                 scope: this
11735             };
11736             if(this.useAjax){
11737                 Roo.applyIf(o, this.conn);
11738                 if(this.activeRequest){
11739                     Roo.Ajax.abort(this.activeRequest);
11740                 }
11741                 this.activeRequest = Roo.Ajax.request(o);
11742             }else{
11743                 this.conn.request(o);
11744             }
11745         }else{
11746             callback.call(scope||this, null, arg, false);
11747         }
11748     },
11749
11750     // private
11751     loadResponse : function(o, success, response){
11752         delete this.activeRequest;
11753         if(!success){
11754             this.fireEvent("loadexception", this, o, response);
11755             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11756             return;
11757         }
11758         var result;
11759         try {
11760             result = o.reader.read(response);
11761         }catch(e){
11762             this.fireEvent("loadexception", this, o, response, e);
11763             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11764             return;
11765         }
11766         
11767         this.fireEvent("load", this, o, o.request.arg);
11768         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11769     },
11770
11771     // private
11772     update : function(dataSet){
11773
11774     },
11775
11776     // private
11777     updateResponse : function(dataSet){
11778
11779     }
11780 });/*
11781  * Based on:
11782  * Ext JS Library 1.1.1
11783  * Copyright(c) 2006-2007, Ext JS, LLC.
11784  *
11785  * Originally Released Under LGPL - original licence link has changed is not relivant.
11786  *
11787  * Fork - LGPL
11788  * <script type="text/javascript">
11789  */
11790
11791 /**
11792  * @class Roo.data.ScriptTagProxy
11793  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11794  * other than the originating domain of the running page.<br><br>
11795  * <p>
11796  * <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
11797  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11798  * <p>
11799  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11800  * source code that is used as the source inside a &lt;script> tag.<br><br>
11801  * <p>
11802  * In order for the browser to process the returned data, the server must wrap the data object
11803  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11804  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11805  * depending on whether the callback name was passed:
11806  * <p>
11807  * <pre><code>
11808 boolean scriptTag = false;
11809 String cb = request.getParameter("callback");
11810 if (cb != null) {
11811     scriptTag = true;
11812     response.setContentType("text/javascript");
11813 } else {
11814     response.setContentType("application/x-json");
11815 }
11816 Writer out = response.getWriter();
11817 if (scriptTag) {
11818     out.write(cb + "(");
11819 }
11820 out.print(dataBlock.toJsonString());
11821 if (scriptTag) {
11822     out.write(");");
11823 }
11824 </pre></code>
11825  *
11826  * @constructor
11827  * @param {Object} config A configuration object.
11828  */
11829 Roo.data.ScriptTagProxy = function(config){
11830     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11831     Roo.apply(this, config);
11832     this.head = document.getElementsByTagName("head")[0];
11833 };
11834
11835 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11836
11837 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11838     /**
11839      * @cfg {String} url The URL from which to request the data object.
11840      */
11841     /**
11842      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11843      */
11844     timeout : 30000,
11845     /**
11846      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11847      * the server the name of the callback function set up by the load call to process the returned data object.
11848      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11849      * javascript output which calls this named function passing the data object as its only parameter.
11850      */
11851     callbackParam : "callback",
11852     /**
11853      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11854      * name to the request.
11855      */
11856     nocache : true,
11857
11858     /**
11859      * Load data from the configured URL, read the data object into
11860      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11861      * process that block using the passed callback.
11862      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11863      * for the request to the remote server.
11864      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11865      * object into a block of Roo.data.Records.
11866      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11867      * The function must be passed <ul>
11868      * <li>The Record block object</li>
11869      * <li>The "arg" argument from the load function</li>
11870      * <li>A boolean success indicator</li>
11871      * </ul>
11872      * @param {Object} scope The scope in which to call the callback
11873      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11874      */
11875     load : function(params, reader, callback, scope, arg){
11876         if(this.fireEvent("beforeload", this, params) !== false){
11877
11878             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11879
11880             var url = this.url;
11881             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11882             if(this.nocache){
11883                 url += "&_dc=" + (new Date().getTime());
11884             }
11885             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11886             var trans = {
11887                 id : transId,
11888                 cb : "stcCallback"+transId,
11889                 scriptId : "stcScript"+transId,
11890                 params : params,
11891                 arg : arg,
11892                 url : url,
11893                 callback : callback,
11894                 scope : scope,
11895                 reader : reader
11896             };
11897             var conn = this;
11898
11899             window[trans.cb] = function(o){
11900                 conn.handleResponse(o, trans);
11901             };
11902
11903             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11904
11905             if(this.autoAbort !== false){
11906                 this.abort();
11907             }
11908
11909             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11910
11911             var script = document.createElement("script");
11912             script.setAttribute("src", url);
11913             script.setAttribute("type", "text/javascript");
11914             script.setAttribute("id", trans.scriptId);
11915             this.head.appendChild(script);
11916
11917             this.trans = trans;
11918         }else{
11919             callback.call(scope||this, null, arg, false);
11920         }
11921     },
11922
11923     // private
11924     isLoading : function(){
11925         return this.trans ? true : false;
11926     },
11927
11928     /**
11929      * Abort the current server request.
11930      */
11931     abort : function(){
11932         if(this.isLoading()){
11933             this.destroyTrans(this.trans);
11934         }
11935     },
11936
11937     // private
11938     destroyTrans : function(trans, isLoaded){
11939         this.head.removeChild(document.getElementById(trans.scriptId));
11940         clearTimeout(trans.timeoutId);
11941         if(isLoaded){
11942             window[trans.cb] = undefined;
11943             try{
11944                 delete window[trans.cb];
11945             }catch(e){}
11946         }else{
11947             // if hasn't been loaded, wait for load to remove it to prevent script error
11948             window[trans.cb] = function(){
11949                 window[trans.cb] = undefined;
11950                 try{
11951                     delete window[trans.cb];
11952                 }catch(e){}
11953             };
11954         }
11955     },
11956
11957     // private
11958     handleResponse : function(o, trans){
11959         this.trans = false;
11960         this.destroyTrans(trans, true);
11961         var result;
11962         try {
11963             result = trans.reader.readRecords(o);
11964         }catch(e){
11965             this.fireEvent("loadexception", this, o, trans.arg, e);
11966             trans.callback.call(trans.scope||window, null, trans.arg, false);
11967             return;
11968         }
11969         this.fireEvent("load", this, o, trans.arg);
11970         trans.callback.call(trans.scope||window, result, trans.arg, true);
11971     },
11972
11973     // private
11974     handleFailure : function(trans){
11975         this.trans = false;
11976         this.destroyTrans(trans, false);
11977         this.fireEvent("loadexception", this, null, trans.arg);
11978         trans.callback.call(trans.scope||window, null, trans.arg, false);
11979     }
11980 });/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990
11991 /**
11992  * @class Roo.data.JsonReader
11993  * @extends Roo.data.DataReader
11994  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11995  * based on mappings in a provided Roo.data.Record constructor.
11996  * 
11997  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11998  * in the reply previously. 
11999  * 
12000  * <p>
12001  * Example code:
12002  * <pre><code>
12003 var RecordDef = Roo.data.Record.create([
12004     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12005     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12006 ]);
12007 var myReader = new Roo.data.JsonReader({
12008     totalProperty: "results",    // The property which contains the total dataset size (optional)
12009     root: "rows",                // The property which contains an Array of row objects
12010     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12011 }, RecordDef);
12012 </code></pre>
12013  * <p>
12014  * This would consume a JSON file like this:
12015  * <pre><code>
12016 { 'results': 2, 'rows': [
12017     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12018     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12019 }
12020 </code></pre>
12021  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12022  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12023  * paged from the remote server.
12024  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12025  * @cfg {String} root name of the property which contains the Array of row objects.
12026  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12027  * @cfg {Array} fields Array of field definition objects
12028  * @constructor
12029  * Create a new JsonReader
12030  * @param {Object} meta Metadata configuration options
12031  * @param {Object} recordType Either an Array of field definition objects,
12032  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12033  */
12034 Roo.data.JsonReader = function(meta, recordType){
12035     
12036     meta = meta || {};
12037     // set some defaults:
12038     Roo.applyIf(meta, {
12039         totalProperty: 'total',
12040         successProperty : 'success',
12041         root : 'data',
12042         id : 'id'
12043     });
12044     
12045     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12046 };
12047 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12048     
12049     /**
12050      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12051      * Used by Store query builder to append _requestMeta to params.
12052      * 
12053      */
12054     metaFromRemote : false,
12055     /**
12056      * This method is only used by a DataProxy which has retrieved data from a remote server.
12057      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12058      * @return {Object} data A data block which is used by an Roo.data.Store object as
12059      * a cache of Roo.data.Records.
12060      */
12061     read : function(response){
12062         var json = response.responseText;
12063        
12064         var o = /* eval:var:o */ eval("("+json+")");
12065         if(!o) {
12066             throw {message: "JsonReader.read: Json object not found"};
12067         }
12068         
12069         if(o.metaData){
12070             
12071             delete this.ef;
12072             this.metaFromRemote = true;
12073             this.meta = o.metaData;
12074             this.recordType = Roo.data.Record.create(o.metaData.fields);
12075             this.onMetaChange(this.meta, this.recordType, o);
12076         }
12077         return this.readRecords(o);
12078     },
12079
12080     // private function a store will implement
12081     onMetaChange : function(meta, recordType, o){
12082
12083     },
12084
12085     /**
12086          * @ignore
12087          */
12088     simpleAccess: function(obj, subsc) {
12089         return obj[subsc];
12090     },
12091
12092         /**
12093          * @ignore
12094          */
12095     getJsonAccessor: function(){
12096         var re = /[\[\.]/;
12097         return function(expr) {
12098             try {
12099                 return(re.test(expr))
12100                     ? new Function("obj", "return obj." + expr)
12101                     : function(obj){
12102                         return obj[expr];
12103                     };
12104             } catch(e){}
12105             return Roo.emptyFn;
12106         };
12107     }(),
12108
12109     /**
12110      * Create a data block containing Roo.data.Records from an XML document.
12111      * @param {Object} o An object which contains an Array of row objects in the property specified
12112      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12113      * which contains the total size of the dataset.
12114      * @return {Object} data A data block which is used by an Roo.data.Store object as
12115      * a cache of Roo.data.Records.
12116      */
12117     readRecords : function(o){
12118         /**
12119          * After any data loads, the raw JSON data is available for further custom processing.
12120          * @type Object
12121          */
12122         this.o = o;
12123         var s = this.meta, Record = this.recordType,
12124             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12125
12126 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12127         if (!this.ef) {
12128             if(s.totalProperty) {
12129                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12130                 }
12131                 if(s.successProperty) {
12132                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12133                 }
12134                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12135                 if (s.id) {
12136                         var g = this.getJsonAccessor(s.id);
12137                         this.getId = function(rec) {
12138                                 var r = g(rec);  
12139                                 return (r === undefined || r === "") ? null : r;
12140                         };
12141                 } else {
12142                         this.getId = function(){return null;};
12143                 }
12144             this.ef = [];
12145             for(var jj = 0; jj < fl; jj++){
12146                 f = fi[jj];
12147                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12148                 this.ef[jj] = this.getJsonAccessor(map);
12149             }
12150         }
12151
12152         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12153         if(s.totalProperty){
12154             var vt = parseInt(this.getTotal(o), 10);
12155             if(!isNaN(vt)){
12156                 totalRecords = vt;
12157             }
12158         }
12159         if(s.successProperty){
12160             var vs = this.getSuccess(o);
12161             if(vs === false || vs === 'false'){
12162                 success = false;
12163             }
12164         }
12165         var records = [];
12166         for(var i = 0; i < c; i++){
12167                 var n = root[i];
12168             var values = {};
12169             var id = this.getId(n);
12170             for(var j = 0; j < fl; j++){
12171                 f = fi[j];
12172             var v = this.ef[j](n);
12173             if (!f.convert) {
12174                 Roo.log('missing convert for ' + f.name);
12175                 Roo.log(f);
12176                 continue;
12177             }
12178             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12179             }
12180             var record = new Record(values, id);
12181             record.json = n;
12182             records[i] = record;
12183         }
12184         return {
12185             raw : o,
12186             success : success,
12187             records : records,
12188             totalRecords : totalRecords
12189         };
12190     }
12191 });/*
12192  * Based on:
12193  * Ext JS Library 1.1.1
12194  * Copyright(c) 2006-2007, Ext JS, LLC.
12195  *
12196  * Originally Released Under LGPL - original licence link has changed is not relivant.
12197  *
12198  * Fork - LGPL
12199  * <script type="text/javascript">
12200  */
12201
12202 /**
12203  * @class Roo.data.ArrayReader
12204  * @extends Roo.data.DataReader
12205  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12206  * Each element of that Array represents a row of data fields. The
12207  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12208  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12209  * <p>
12210  * Example code:.
12211  * <pre><code>
12212 var RecordDef = Roo.data.Record.create([
12213     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12214     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12215 ]);
12216 var myReader = new Roo.data.ArrayReader({
12217     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12218 }, RecordDef);
12219 </code></pre>
12220  * <p>
12221  * This would consume an Array like this:
12222  * <pre><code>
12223 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12224   </code></pre>
12225  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12226  * @constructor
12227  * Create a new JsonReader
12228  * @param {Object} meta Metadata configuration options.
12229  * @param {Object} recordType Either an Array of field definition objects
12230  * as specified to {@link Roo.data.Record#create},
12231  * or an {@link Roo.data.Record} object
12232  * created using {@link Roo.data.Record#create}.
12233  */
12234 Roo.data.ArrayReader = function(meta, recordType){
12235     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12236 };
12237
12238 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12239     /**
12240      * Create a data block containing Roo.data.Records from an XML document.
12241      * @param {Object} o An Array of row objects which represents the dataset.
12242      * @return {Object} data A data block which is used by an Roo.data.Store object as
12243      * a cache of Roo.data.Records.
12244      */
12245     readRecords : function(o){
12246         var sid = this.meta ? this.meta.id : null;
12247         var recordType = this.recordType, fields = recordType.prototype.fields;
12248         var records = [];
12249         var root = o;
12250             for(var i = 0; i < root.length; i++){
12251                     var n = root[i];
12252                 var values = {};
12253                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12254                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12255                 var f = fields.items[j];
12256                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12257                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12258                 v = f.convert(v);
12259                 values[f.name] = v;
12260             }
12261                 var record = new recordType(values, id);
12262                 record.json = n;
12263                 records[records.length] = record;
12264             }
12265             return {
12266                 records : records,
12267                 totalRecords : records.length
12268             };
12269     }
12270 });/*
12271  * - LGPL
12272  * * 
12273  */
12274
12275 /**
12276  * @class Roo.bootstrap.ComboBox
12277  * @extends Roo.bootstrap.TriggerField
12278  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12279  * @cfg {Boolean} append (true|false) default false
12280  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12281  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12282  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12283  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12284  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12285  * @cfg {Boolean} animate default true
12286  * @cfg {Boolean} emptyResultText only for touch device
12287  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12288  * @constructor
12289  * Create a new ComboBox.
12290  * @param {Object} config Configuration options
12291  */
12292 Roo.bootstrap.ComboBox = function(config){
12293     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12294     this.addEvents({
12295         /**
12296          * @event expand
12297          * Fires when the dropdown list is expanded
12298              * @param {Roo.bootstrap.ComboBox} combo This combo box
12299              */
12300         'expand' : true,
12301         /**
12302          * @event collapse
12303          * Fires when the dropdown list is collapsed
12304              * @param {Roo.bootstrap.ComboBox} combo This combo box
12305              */
12306         'collapse' : true,
12307         /**
12308          * @event beforeselect
12309          * Fires before a list item is selected. Return false to cancel the selection.
12310              * @param {Roo.bootstrap.ComboBox} combo This combo box
12311              * @param {Roo.data.Record} record The data record returned from the underlying store
12312              * @param {Number} index The index of the selected item in the dropdown list
12313              */
12314         'beforeselect' : true,
12315         /**
12316          * @event select
12317          * Fires when a list item is selected
12318              * @param {Roo.bootstrap.ComboBox} combo This combo box
12319              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12320              * @param {Number} index The index of the selected item in the dropdown list
12321              */
12322         'select' : true,
12323         /**
12324          * @event beforequery
12325          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12326          * The event object passed has these properties:
12327              * @param {Roo.bootstrap.ComboBox} combo This combo box
12328              * @param {String} query The query
12329              * @param {Boolean} forceAll true to force "all" query
12330              * @param {Boolean} cancel true to cancel the query
12331              * @param {Object} e The query event object
12332              */
12333         'beforequery': true,
12334          /**
12335          * @event add
12336          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12337              * @param {Roo.bootstrap.ComboBox} combo This combo box
12338              */
12339         'add' : true,
12340         /**
12341          * @event edit
12342          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12343              * @param {Roo.bootstrap.ComboBox} combo This combo box
12344              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12345              */
12346         'edit' : true,
12347         /**
12348          * @event remove
12349          * Fires when the remove value from the combobox array
12350              * @param {Roo.bootstrap.ComboBox} combo This combo box
12351              */
12352         'remove' : true,
12353         /**
12354          * @event afterremove
12355          * Fires when the remove value from the combobox array
12356              * @param {Roo.bootstrap.ComboBox} combo This combo box
12357              */
12358         'afterremove' : true,
12359         /**
12360          * @event specialfilter
12361          * Fires when specialfilter
12362             * @param {Roo.bootstrap.ComboBox} combo This combo box
12363             */
12364         'specialfilter' : true,
12365         /**
12366          * @event tick
12367          * Fires when tick the element
12368             * @param {Roo.bootstrap.ComboBox} combo This combo box
12369             */
12370         'tick' : true,
12371         /**
12372          * @event touchviewdisplay
12373          * Fires when touch view require special display (default is using displayField)
12374             * @param {Roo.bootstrap.ComboBox} combo This combo box
12375             * @param {Object} cfg set html .
12376             */
12377         'touchviewdisplay' : true
12378         
12379     });
12380     
12381     this.item = [];
12382     this.tickItems = [];
12383     
12384     this.selectedIndex = -1;
12385     if(this.mode == 'local'){
12386         if(config.queryDelay === undefined){
12387             this.queryDelay = 10;
12388         }
12389         if(config.minChars === undefined){
12390             this.minChars = 0;
12391         }
12392     }
12393 };
12394
12395 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12396      
12397     /**
12398      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12399      * rendering into an Roo.Editor, defaults to false)
12400      */
12401     /**
12402      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12403      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12404      */
12405     /**
12406      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12407      */
12408     /**
12409      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12410      * the dropdown list (defaults to undefined, with no header element)
12411      */
12412
12413      /**
12414      * @cfg {String/Roo.Template} tpl The template to use to render the output
12415      */
12416      
12417      /**
12418      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12419      */
12420     listWidth: undefined,
12421     /**
12422      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12423      * mode = 'remote' or 'text' if mode = 'local')
12424      */
12425     displayField: undefined,
12426     
12427     /**
12428      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12429      * mode = 'remote' or 'value' if mode = 'local'). 
12430      * Note: use of a valueField requires the user make a selection
12431      * in order for a value to be mapped.
12432      */
12433     valueField: undefined,
12434     /**
12435      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12436      */
12437     modalTitle : '',
12438     
12439     /**
12440      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12441      * field's data value (defaults to the underlying DOM element's name)
12442      */
12443     hiddenName: undefined,
12444     /**
12445      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12446      */
12447     listClass: '',
12448     /**
12449      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12450      */
12451     selectedClass: 'active',
12452     
12453     /**
12454      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12455      */
12456     shadow:'sides',
12457     /**
12458      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12459      * anchor positions (defaults to 'tl-bl')
12460      */
12461     listAlign: 'tl-bl?',
12462     /**
12463      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12464      */
12465     maxHeight: 300,
12466     /**
12467      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12468      * query specified by the allQuery config option (defaults to 'query')
12469      */
12470     triggerAction: 'query',
12471     /**
12472      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12473      * (defaults to 4, does not apply if editable = false)
12474      */
12475     minChars : 4,
12476     /**
12477      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12478      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12479      */
12480     typeAhead: false,
12481     /**
12482      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12483      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12484      */
12485     queryDelay: 500,
12486     /**
12487      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12488      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12489      */
12490     pageSize: 0,
12491     /**
12492      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12493      * when editable = true (defaults to false)
12494      */
12495     selectOnFocus:false,
12496     /**
12497      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12498      */
12499     queryParam: 'query',
12500     /**
12501      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12502      * when mode = 'remote' (defaults to 'Loading...')
12503      */
12504     loadingText: 'Loading...',
12505     /**
12506      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12507      */
12508     resizable: false,
12509     /**
12510      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12511      */
12512     handleHeight : 8,
12513     /**
12514      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12515      * traditional select (defaults to true)
12516      */
12517     editable: true,
12518     /**
12519      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12520      */
12521     allQuery: '',
12522     /**
12523      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12524      */
12525     mode: 'remote',
12526     /**
12527      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12528      * listWidth has a higher value)
12529      */
12530     minListWidth : 70,
12531     /**
12532      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12533      * allow the user to set arbitrary text into the field (defaults to false)
12534      */
12535     forceSelection:false,
12536     /**
12537      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12538      * if typeAhead = true (defaults to 250)
12539      */
12540     typeAheadDelay : 250,
12541     /**
12542      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12543      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12544      */
12545     valueNotFoundText : undefined,
12546     /**
12547      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12548      */
12549     blockFocus : false,
12550     
12551     /**
12552      * @cfg {Boolean} disableClear Disable showing of clear button.
12553      */
12554     disableClear : false,
12555     /**
12556      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12557      */
12558     alwaysQuery : false,
12559     
12560     /**
12561      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12562      */
12563     multiple : false,
12564     
12565     /**
12566      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12567      */
12568     invalidClass : "has-warning",
12569     
12570     /**
12571      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12572      */
12573     validClass : "has-success",
12574     
12575     /**
12576      * @cfg {Boolean} specialFilter (true|false) special filter default false
12577      */
12578     specialFilter : false,
12579     
12580     /**
12581      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12582      */
12583     mobileTouchView : true,
12584     
12585     /**
12586      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12587      */
12588     useNativeIOS : false,
12589     
12590     ios_options : false,
12591     
12592     //private
12593     addicon : false,
12594     editicon: false,
12595     
12596     page: 0,
12597     hasQuery: false,
12598     append: false,
12599     loadNext: false,
12600     autoFocus : true,
12601     tickable : false,
12602     btnPosition : 'right',
12603     triggerList : true,
12604     showToggleBtn : true,
12605     animate : true,
12606     emptyResultText: 'Empty',
12607     triggerText : 'Select',
12608     
12609     // element that contains real text value.. (when hidden is used..)
12610     
12611     getAutoCreate : function()
12612     {   
12613         var cfg = false;
12614         //render
12615         /*
12616          * Render classic select for iso
12617          */
12618         
12619         if(Roo.isIOS && this.useNativeIOS){
12620             cfg = this.getAutoCreateNativeIOS();
12621             return cfg;
12622         }
12623         
12624         /*
12625          * Touch Devices
12626          */
12627         
12628         if(Roo.isTouch && this.mobileTouchView){
12629             cfg = this.getAutoCreateTouchView();
12630             return cfg;;
12631         }
12632         
12633         /*
12634          *  Normal ComboBox
12635          */
12636         if(!this.tickable){
12637             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12638             if(this.name == 'info_year_invest_id_display_name'){
12639                 Roo.log('cfg.................................................');
12640                 Roo.log(cfg);
12641             }
12642             return cfg;
12643         }
12644         
12645         /*
12646          *  ComboBox with tickable selections
12647          */
12648              
12649         var align = this.labelAlign || this.parentLabelAlign();
12650         
12651         cfg = {
12652             cls : 'form-group roo-combobox-tickable' //input-group
12653         };
12654         
12655         var btn_text_select = '';
12656         var btn_text_done = '';
12657         var btn_text_cancel = '';
12658         
12659         if (this.btn_text_show) {
12660             btn_text_select = 'Select';
12661             btn_text_done = 'Done';
12662             btn_text_cancel = 'Cancel'; 
12663         }
12664         
12665         var buttons = {
12666             tag : 'div',
12667             cls : 'tickable-buttons',
12668             cn : [
12669                 {
12670                     tag : 'button',
12671                     type : 'button',
12672                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12673                     //html : this.triggerText
12674                     html: btn_text_select
12675                 },
12676                 {
12677                     tag : 'button',
12678                     type : 'button',
12679                     name : 'ok',
12680                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12681                     //html : 'Done'
12682                     html: btn_text_done
12683                 },
12684                 {
12685                     tag : 'button',
12686                     type : 'button',
12687                     name : 'cancel',
12688                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12689                     //html : 'Cancel'
12690                     html: btn_text_cancel
12691                 }
12692             ]
12693         };
12694         
12695         if(this.editable){
12696             buttons.cn.unshift({
12697                 tag: 'input',
12698                 cls: 'roo-select2-search-field-input'
12699             });
12700         }
12701         
12702         var _this = this;
12703         
12704         Roo.each(buttons.cn, function(c){
12705             if (_this.size) {
12706                 c.cls += ' btn-' + _this.size;
12707             }
12708
12709             if (_this.disabled) {
12710                 c.disabled = true;
12711             }
12712         });
12713         
12714         var box = {
12715             tag: 'div',
12716             cn: [
12717                 {
12718                     tag: 'input',
12719                     type : 'hidden',
12720                     cls: 'form-hidden-field'
12721                 },
12722                 {
12723                     tag: 'ul',
12724                     cls: 'roo-select2-choices',
12725                     cn:[
12726                         {
12727                             tag: 'li',
12728                             cls: 'roo-select2-search-field',
12729                             cn: [
12730                                 buttons
12731                             ]
12732                         }
12733                     ]
12734                 }
12735             ]
12736         };
12737         
12738         var combobox = {
12739             cls: 'roo-select2-container input-group roo-select2-container-multi',
12740             cn: [
12741                 box
12742 //                {
12743 //                    tag: 'ul',
12744 //                    cls: 'typeahead typeahead-long dropdown-menu',
12745 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12746 //                }
12747             ]
12748         };
12749         
12750         if(this.hasFeedback && !this.allowBlank){
12751             
12752             var feedback = {
12753                 tag: 'span',
12754                 cls: 'glyphicon form-control-feedback'
12755             };
12756
12757             combobox.cn.push(feedback);
12758         }
12759         
12760         
12761         if (align ==='left' && this.fieldLabel.length) {
12762             
12763             cfg.cls += ' roo-form-group-label-left';
12764             
12765             cfg.cn = [
12766                 {
12767                     tag : 'i',
12768                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12769                     tooltip : 'This field is required'
12770                 },
12771                 {
12772                     tag: 'label',
12773                     'for' :  id,
12774                     cls : 'control-label',
12775                     html : this.fieldLabel
12776
12777                 },
12778                 {
12779                     cls : "", 
12780                     cn: [
12781                         combobox
12782                     ]
12783                 }
12784
12785             ];
12786             
12787             var labelCfg = cfg.cn[1];
12788             var contentCfg = cfg.cn[2];
12789             
12790
12791             if(this.indicatorpos == 'right'){
12792                 
12793                 cfg.cn = [
12794                     {
12795                         tag: 'label',
12796                         'for' :  id,
12797                         cls : 'control-label',
12798                         cn : [
12799                             {
12800                                 tag : 'span',
12801                                 html : this.fieldLabel
12802                             },
12803                             {
12804                                 tag : 'i',
12805                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12806                                 tooltip : 'This field is required'
12807                             }
12808                         ]
12809                     },
12810                     {
12811                         cls : "",
12812                         cn: [
12813                             combobox
12814                         ]
12815                     }
12816
12817                 ];
12818                 
12819                 
12820                 
12821                 labelCfg = cfg.cn[0];
12822                 contentCfg = cfg.cn[1];
12823             
12824             }
12825             
12826             if(this.labelWidth > 12){
12827                 labelCfg.style = "width: " + this.labelWidth + 'px';
12828             }
12829             
12830             if(this.labelWidth < 13 && this.labelmd == 0){
12831                 this.labelmd = this.labelWidth;
12832             }
12833             
12834             if(this.labellg > 0){
12835                 labelCfg.cls += ' col-lg-' + this.labellg;
12836                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12837             }
12838             
12839             if(this.labelmd > 0){
12840                 labelCfg.cls += ' col-md-' + this.labelmd;
12841                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12842             }
12843             
12844             if(this.labelsm > 0){
12845                 labelCfg.cls += ' col-sm-' + this.labelsm;
12846                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12847             }
12848             
12849             if(this.labelxs > 0){
12850                 labelCfg.cls += ' col-xs-' + this.labelxs;
12851                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12852             }
12853                 
12854                 
12855         } else if ( this.fieldLabel.length) {
12856 //                Roo.log(" label");
12857                  cfg.cn = [
12858                     {
12859                         tag : 'i',
12860                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12861                         tooltip : 'This field is required'
12862                     },
12863                     {
12864                         tag: 'label',
12865                         //cls : 'input-group-addon',
12866                         html : this.fieldLabel
12867                         
12868                     },
12869                     
12870                     combobox
12871                     
12872                 ];
12873                 
12874                 if(this.indicatorpos == 'right'){
12875                     
12876                     cfg.cn = [
12877                         {
12878                             tag: 'label',
12879                             //cls : 'input-group-addon',
12880                             cn : [
12881                                 {
12882                                     tag : 'span',
12883                                     html : this.fieldLabel
12884                                 },
12885                                 {
12886                                     tag : 'i',
12887                                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12888                                     tooltip : 'This field is required'
12889                                 }
12890                             ]
12891                         },
12892                         
12893                         
12894                         
12895                         combobox
12896
12897                     ];
12898                 
12899                 }
12900
12901         } else {
12902             
12903 //                Roo.log(" no label && no align");
12904                 cfg = combobox
12905                      
12906                 
12907         }
12908          
12909         var settings=this;
12910         ['xs','sm','md','lg'].map(function(size){
12911             if (settings[size]) {
12912                 cfg.cls += ' col-' + size + '-' + settings[size];
12913             }
12914         });
12915         
12916         return cfg;
12917         
12918     },
12919     
12920     _initEventsCalled : false,
12921     
12922     // private
12923     initEvents: function()
12924     {   
12925         if (this._initEventsCalled) { // as we call render... prevent looping...
12926             return;
12927         }
12928         this._initEventsCalled = true;
12929         
12930         if (!this.store) {
12931             throw "can not find store for combo";
12932         }
12933         
12934         this.store = Roo.factory(this.store, Roo.data);
12935         
12936         // if we are building from html. then this element is so complex, that we can not really
12937         // use the rendered HTML.
12938         // so we have to trash and replace the previous code.
12939         if (Roo.XComponent.build_from_html) {
12940             
12941             // remove this element....
12942             var e = this.el.dom, k=0;
12943             while (e ) { e = e.previousSibling;  ++k;}
12944
12945             this.el.remove();
12946             
12947             this.el=false;
12948             this.rendered = false;
12949             
12950             this.render(this.parent().getChildContainer(true), k);
12951             
12952             
12953             
12954         }
12955         
12956         if(Roo.isIOS && this.useNativeIOS){
12957             this.initIOSView();
12958             return;
12959         }
12960         
12961         /*
12962          * Touch Devices
12963          */
12964         
12965         if(Roo.isTouch && this.mobileTouchView){
12966             this.initTouchView();
12967             return;
12968         }
12969         
12970         if(this.tickable){
12971             this.initTickableEvents();
12972             return;
12973         }
12974         
12975         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12976         
12977         if(this.hiddenName){
12978             
12979             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12980             
12981             this.hiddenField.dom.value =
12982                 this.hiddenValue !== undefined ? this.hiddenValue :
12983                 this.value !== undefined ? this.value : '';
12984
12985             // prevent input submission
12986             this.el.dom.removeAttribute('name');
12987             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12988              
12989              
12990         }
12991         //if(Roo.isGecko){
12992         //    this.el.dom.setAttribute('autocomplete', 'off');
12993         //}
12994         
12995         var cls = 'x-combo-list';
12996         
12997         //this.list = new Roo.Layer({
12998         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12999         //});
13000         
13001         var _this = this;
13002         
13003         (function(){
13004             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13005             _this.list.setWidth(lw);
13006         }).defer(100);
13007         
13008         this.list.on('mouseover', this.onViewOver, this);
13009         this.list.on('mousemove', this.onViewMove, this);
13010         
13011         this.list.on('scroll', this.onViewScroll, this);
13012         
13013         /*
13014         this.list.swallowEvent('mousewheel');
13015         this.assetHeight = 0;
13016
13017         if(this.title){
13018             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13019             this.assetHeight += this.header.getHeight();
13020         }
13021
13022         this.innerList = this.list.createChild({cls:cls+'-inner'});
13023         this.innerList.on('mouseover', this.onViewOver, this);
13024         this.innerList.on('mousemove', this.onViewMove, this);
13025         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13026         
13027         if(this.allowBlank && !this.pageSize && !this.disableClear){
13028             this.footer = this.list.createChild({cls:cls+'-ft'});
13029             this.pageTb = new Roo.Toolbar(this.footer);
13030            
13031         }
13032         if(this.pageSize){
13033             this.footer = this.list.createChild({cls:cls+'-ft'});
13034             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13035                     {pageSize: this.pageSize});
13036             
13037         }
13038         
13039         if (this.pageTb && this.allowBlank && !this.disableClear) {
13040             var _this = this;
13041             this.pageTb.add(new Roo.Toolbar.Fill(), {
13042                 cls: 'x-btn-icon x-btn-clear',
13043                 text: '&#160;',
13044                 handler: function()
13045                 {
13046                     _this.collapse();
13047                     _this.clearValue();
13048                     _this.onSelect(false, -1);
13049                 }
13050             });
13051         }
13052         if (this.footer) {
13053             this.assetHeight += this.footer.getHeight();
13054         }
13055         */
13056             
13057         if(!this.tpl){
13058             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13059         }
13060
13061         this.view = new Roo.View(this.list, this.tpl, {
13062             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13063         });
13064         //this.view.wrapEl.setDisplayed(false);
13065         this.view.on('click', this.onViewClick, this);
13066         
13067         
13068         
13069         this.store.on('beforeload', this.onBeforeLoad, this);
13070         this.store.on('load', this.onLoad, this);
13071         this.store.on('loadexception', this.onLoadException, this);
13072         /*
13073         if(this.resizable){
13074             this.resizer = new Roo.Resizable(this.list,  {
13075                pinned:true, handles:'se'
13076             });
13077             this.resizer.on('resize', function(r, w, h){
13078                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13079                 this.listWidth = w;
13080                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13081                 this.restrictHeight();
13082             }, this);
13083             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13084         }
13085         */
13086         if(!this.editable){
13087             this.editable = true;
13088             this.setEditable(false);
13089         }
13090         
13091         /*
13092         
13093         if (typeof(this.events.add.listeners) != 'undefined') {
13094             
13095             this.addicon = this.wrap.createChild(
13096                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13097        
13098             this.addicon.on('click', function(e) {
13099                 this.fireEvent('add', this);
13100             }, this);
13101         }
13102         if (typeof(this.events.edit.listeners) != 'undefined') {
13103             
13104             this.editicon = this.wrap.createChild(
13105                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13106             if (this.addicon) {
13107                 this.editicon.setStyle('margin-left', '40px');
13108             }
13109             this.editicon.on('click', function(e) {
13110                 
13111                 // we fire even  if inothing is selected..
13112                 this.fireEvent('edit', this, this.lastData );
13113                 
13114             }, this);
13115         }
13116         */
13117         
13118         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13119             "up" : function(e){
13120                 this.inKeyMode = true;
13121                 this.selectPrev();
13122             },
13123
13124             "down" : function(e){
13125                 if(!this.isExpanded()){
13126                     this.onTriggerClick();
13127                 }else{
13128                     this.inKeyMode = true;
13129                     this.selectNext();
13130                 }
13131             },
13132
13133             "enter" : function(e){
13134 //                this.onViewClick();
13135                 //return true;
13136                 this.collapse();
13137                 
13138                 if(this.fireEvent("specialkey", this, e)){
13139                     this.onViewClick(false);
13140                 }
13141                 
13142                 return true;
13143             },
13144
13145             "esc" : function(e){
13146                 this.collapse();
13147             },
13148
13149             "tab" : function(e){
13150                 this.collapse();
13151                 
13152                 if(this.fireEvent("specialkey", this, e)){
13153                     this.onViewClick(false);
13154                 }
13155                 
13156                 return true;
13157             },
13158
13159             scope : this,
13160
13161             doRelay : function(foo, bar, hname){
13162                 if(hname == 'down' || this.scope.isExpanded()){
13163                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13164                 }
13165                 return true;
13166             },
13167
13168             forceKeyDown: true
13169         });
13170         
13171         
13172         this.queryDelay = Math.max(this.queryDelay || 10,
13173                 this.mode == 'local' ? 10 : 250);
13174         
13175         
13176         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13177         
13178         if(this.typeAhead){
13179             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13180         }
13181         if(this.editable !== false){
13182             this.inputEl().on("keyup", this.onKeyUp, this);
13183         }
13184         if(this.forceSelection){
13185             this.inputEl().on('blur', this.doForce, this);
13186         }
13187         
13188         if(this.multiple){
13189             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13190             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13191         }
13192     },
13193     
13194     initTickableEvents: function()
13195     {   
13196         this.createList();
13197         
13198         if(this.hiddenName){
13199             
13200             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13201             
13202             this.hiddenField.dom.value =
13203                 this.hiddenValue !== undefined ? this.hiddenValue :
13204                 this.value !== undefined ? this.value : '';
13205
13206             // prevent input submission
13207             this.el.dom.removeAttribute('name');
13208             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13209              
13210              
13211         }
13212         
13213 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13214         
13215         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13216         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13217         if(this.triggerList){
13218             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13219         }
13220          
13221         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13222         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13223         
13224         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13225         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13226         
13227         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13228         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13229         
13230         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13231         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13232         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13233         
13234         this.okBtn.hide();
13235         this.cancelBtn.hide();
13236         
13237         var _this = this;
13238         
13239         (function(){
13240             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13241             _this.list.setWidth(lw);
13242         }).defer(100);
13243         
13244         this.list.on('mouseover', this.onViewOver, this);
13245         this.list.on('mousemove', this.onViewMove, this);
13246         
13247         this.list.on('scroll', this.onViewScroll, this);
13248         
13249         if(!this.tpl){
13250             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></div></li>';
13251         }
13252
13253         this.view = new Roo.View(this.list, this.tpl, {
13254             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13255         });
13256         
13257         //this.view.wrapEl.setDisplayed(false);
13258         this.view.on('click', this.onViewClick, this);
13259         
13260         
13261         
13262         this.store.on('beforeload', this.onBeforeLoad, this);
13263         this.store.on('load', this.onLoad, this);
13264         this.store.on('loadexception', this.onLoadException, this);
13265         
13266         if(this.editable){
13267             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13268                 "up" : function(e){
13269                     this.inKeyMode = true;
13270                     this.selectPrev();
13271                 },
13272
13273                 "down" : function(e){
13274                     this.inKeyMode = true;
13275                     this.selectNext();
13276                 },
13277
13278                 "enter" : function(e){
13279                     if(this.fireEvent("specialkey", this, e)){
13280                         this.onViewClick(false);
13281                     }
13282                     
13283                     return true;
13284                 },
13285
13286                 "esc" : function(e){
13287                     this.onTickableFooterButtonClick(e, false, false);
13288                 },
13289
13290                 "tab" : function(e){
13291                     this.fireEvent("specialkey", this, e);
13292                     
13293                     this.onTickableFooterButtonClick(e, false, false);
13294                     
13295                     return true;
13296                 },
13297
13298                 scope : this,
13299
13300                 doRelay : function(e, fn, key){
13301                     if(this.scope.isExpanded()){
13302                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13303                     }
13304                     return true;
13305                 },
13306
13307                 forceKeyDown: true
13308             });
13309         }
13310         
13311         this.queryDelay = Math.max(this.queryDelay || 10,
13312                 this.mode == 'local' ? 10 : 250);
13313         
13314         
13315         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13316         
13317         if(this.typeAhead){
13318             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13319         }
13320         
13321         if(this.editable !== false){
13322             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13323         }
13324         
13325         this.indicator = this.indicatorEl();
13326         
13327         if(this.indicator){
13328             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13329             this.indicator.hide();
13330         }
13331         
13332     },
13333
13334     onDestroy : function(){
13335         if(this.view){
13336             this.view.setStore(null);
13337             this.view.el.removeAllListeners();
13338             this.view.el.remove();
13339             this.view.purgeListeners();
13340         }
13341         if(this.list){
13342             this.list.dom.innerHTML  = '';
13343         }
13344         
13345         if(this.store){
13346             this.store.un('beforeload', this.onBeforeLoad, this);
13347             this.store.un('load', this.onLoad, this);
13348             this.store.un('loadexception', this.onLoadException, this);
13349         }
13350         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13351     },
13352
13353     // private
13354     fireKey : function(e){
13355         if(e.isNavKeyPress() && !this.list.isVisible()){
13356             this.fireEvent("specialkey", this, e);
13357         }
13358     },
13359
13360     // private
13361     onResize: function(w, h){
13362 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13363 //        
13364 //        if(typeof w != 'number'){
13365 //            // we do not handle it!?!?
13366 //            return;
13367 //        }
13368 //        var tw = this.trigger.getWidth();
13369 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13370 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13371 //        var x = w - tw;
13372 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13373 //            
13374 //        //this.trigger.setStyle('left', x+'px');
13375 //        
13376 //        if(this.list && this.listWidth === undefined){
13377 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13378 //            this.list.setWidth(lw);
13379 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13380 //        }
13381         
13382     
13383         
13384     },
13385
13386     /**
13387      * Allow or prevent the user from directly editing the field text.  If false is passed,
13388      * the user will only be able to select from the items defined in the dropdown list.  This method
13389      * is the runtime equivalent of setting the 'editable' config option at config time.
13390      * @param {Boolean} value True to allow the user to directly edit the field text
13391      */
13392     setEditable : function(value){
13393         if(value == this.editable){
13394             return;
13395         }
13396         this.editable = value;
13397         if(!value){
13398             this.inputEl().dom.setAttribute('readOnly', true);
13399             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13400             this.inputEl().addClass('x-combo-noedit');
13401         }else{
13402             this.inputEl().dom.setAttribute('readOnly', false);
13403             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13404             this.inputEl().removeClass('x-combo-noedit');
13405         }
13406     },
13407
13408     // private
13409     
13410     onBeforeLoad : function(combo,opts){
13411         if(!this.hasFocus){
13412             return;
13413         }
13414          if (!opts.add) {
13415             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13416          }
13417         this.restrictHeight();
13418         this.selectedIndex = -1;
13419     },
13420
13421     // private
13422     onLoad : function(){
13423         
13424         this.hasQuery = false;
13425         
13426         if(!this.hasFocus){
13427             return;
13428         }
13429         
13430         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13431             this.loading.hide();
13432         }
13433              
13434         if(this.store.getCount() > 0){
13435             this.expand();
13436             this.restrictHeight();
13437             if(this.lastQuery == this.allQuery){
13438                 if(this.editable && !this.tickable){
13439                     this.inputEl().dom.select();
13440                 }
13441                 
13442                 if(
13443                     !this.selectByValue(this.value, true) &&
13444                     this.autoFocus && 
13445                     (
13446                         !this.store.lastOptions ||
13447                         typeof(this.store.lastOptions.add) == 'undefined' || 
13448                         this.store.lastOptions.add != true
13449                     )
13450                 ){
13451                     this.select(0, true);
13452                 }
13453             }else{
13454                 if(this.autoFocus){
13455                     this.selectNext();
13456                 }
13457                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13458                     this.taTask.delay(this.typeAheadDelay);
13459                 }
13460             }
13461         }else{
13462             this.onEmptyResults();
13463         }
13464         
13465         //this.el.focus();
13466     },
13467     // private
13468     onLoadException : function()
13469     {
13470         this.hasQuery = false;
13471         
13472         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13473             this.loading.hide();
13474         }
13475         
13476         if(this.tickable && this.editable){
13477             return;
13478         }
13479         
13480         this.collapse();
13481         // only causes errors at present
13482         //Roo.log(this.store.reader.jsonData);
13483         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13484             // fixme
13485             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13486         //}
13487         
13488         
13489     },
13490     // private
13491     onTypeAhead : function(){
13492         if(this.store.getCount() > 0){
13493             var r = this.store.getAt(0);
13494             var newValue = r.data[this.displayField];
13495             var len = newValue.length;
13496             var selStart = this.getRawValue().length;
13497             
13498             if(selStart != len){
13499                 this.setRawValue(newValue);
13500                 this.selectText(selStart, newValue.length);
13501             }
13502         }
13503     },
13504
13505     // private
13506     onSelect : function(record, index){
13507         
13508         if(this.fireEvent('beforeselect', this, record, index) !== false){
13509         
13510             this.setFromData(index > -1 ? record.data : false);
13511             
13512             this.collapse();
13513             this.fireEvent('select', this, record, index);
13514         }
13515     },
13516
13517     /**
13518      * Returns the currently selected field value or empty string if no value is set.
13519      * @return {String} value The selected value
13520      */
13521     getValue : function()
13522     {
13523         if(Roo.isIOS && this.useNativeIOS){
13524             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13525         }
13526         
13527         if(this.multiple){
13528             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13529         }
13530         
13531         if(this.valueField){
13532             return typeof this.value != 'undefined' ? this.value : '';
13533         }else{
13534             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13535         }
13536     },
13537     
13538     getRawValue : function()
13539     {
13540         if(Roo.isIOS && this.useNativeIOS){
13541             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13542         }
13543         
13544         var v = this.inputEl().getValue();
13545         
13546         return v;
13547     },
13548
13549     /**
13550      * Clears any text/value currently set in the field
13551      */
13552     clearValue : function(){
13553         
13554         if(this.hiddenField){
13555             this.hiddenField.dom.value = '';
13556         }
13557         this.value = '';
13558         this.setRawValue('');
13559         this.lastSelectionText = '';
13560         this.lastData = false;
13561         
13562         var close = this.closeTriggerEl();
13563         
13564         if(close){
13565             close.hide();
13566         }
13567         
13568         this.validate();
13569         
13570     },
13571
13572     /**
13573      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13574      * will be displayed in the field.  If the value does not match the data value of an existing item,
13575      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13576      * Otherwise the field will be blank (although the value will still be set).
13577      * @param {String} value The value to match
13578      */
13579     setValue : function(v)
13580     {
13581         if(Roo.isIOS && this.useNativeIOS){
13582             this.setIOSValue(v);
13583             return;
13584         }
13585         
13586         if(this.multiple){
13587             this.syncValue();
13588             return;
13589         }
13590         
13591         var text = v;
13592         if(this.valueField){
13593             var r = this.findRecord(this.valueField, v);
13594             if(r){
13595                 text = r.data[this.displayField];
13596             }else if(this.valueNotFoundText !== undefined){
13597                 text = this.valueNotFoundText;
13598             }
13599         }
13600         this.lastSelectionText = text;
13601         if(this.hiddenField){
13602             this.hiddenField.dom.value = v;
13603         }
13604         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13605         this.value = v;
13606         
13607         var close = this.closeTriggerEl();
13608         
13609         if(close){
13610             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13611         }
13612         
13613         this.validate();
13614     },
13615     /**
13616      * @property {Object} the last set data for the element
13617      */
13618     
13619     lastData : false,
13620     /**
13621      * Sets the value of the field based on a object which is related to the record format for the store.
13622      * @param {Object} value the value to set as. or false on reset?
13623      */
13624     setFromData : function(o){
13625         
13626         if(this.multiple){
13627             this.addItem(o);
13628             return;
13629         }
13630             
13631         var dv = ''; // display value
13632         var vv = ''; // value value..
13633         this.lastData = o;
13634         if (this.displayField) {
13635             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13636         } else {
13637             // this is an error condition!!!
13638             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13639         }
13640         
13641         if(this.valueField){
13642             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13643         }
13644         
13645         var close = this.closeTriggerEl();
13646         
13647         if(close){
13648             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13649         }
13650         
13651         if(this.hiddenField){
13652             this.hiddenField.dom.value = vv;
13653             
13654             this.lastSelectionText = dv;
13655             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13656             this.value = vv;
13657             return;
13658         }
13659         // no hidden field.. - we store the value in 'value', but still display
13660         // display field!!!!
13661         this.lastSelectionText = dv;
13662         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13663         this.value = vv;
13664         
13665         
13666         
13667     },
13668     // private
13669     reset : function(){
13670         // overridden so that last data is reset..
13671         
13672         if(this.multiple){
13673             this.clearItem();
13674             return;
13675         }
13676         
13677         this.setValue(this.originalValue);
13678         //this.clearInvalid();
13679         this.lastData = false;
13680         if (this.view) {
13681             this.view.clearSelections();
13682         }
13683         
13684         this.validate();
13685     },
13686     // private
13687     findRecord : function(prop, value){
13688         var record;
13689         if(this.store.getCount() > 0){
13690             this.store.each(function(r){
13691                 if(r.data[prop] == value){
13692                     record = r;
13693                     return false;
13694                 }
13695                 return true;
13696             });
13697         }
13698         return record;
13699     },
13700     
13701     getName: function()
13702     {
13703         // returns hidden if it's set..
13704         if (!this.rendered) {return ''};
13705         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13706         
13707     },
13708     // private
13709     onViewMove : function(e, t){
13710         this.inKeyMode = false;
13711     },
13712
13713     // private
13714     onViewOver : function(e, t){
13715         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13716             return;
13717         }
13718         var item = this.view.findItemFromChild(t);
13719         
13720         if(item){
13721             var index = this.view.indexOf(item);
13722             this.select(index, false);
13723         }
13724     },
13725
13726     // private
13727     onViewClick : function(view, doFocus, el, e)
13728     {
13729         var index = this.view.getSelectedIndexes()[0];
13730         
13731         var r = this.store.getAt(index);
13732         
13733         if(this.tickable){
13734             
13735             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13736                 return;
13737             }
13738             
13739             var rm = false;
13740             var _this = this;
13741             
13742             Roo.each(this.tickItems, function(v,k){
13743                 
13744                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13745                     Roo.log(v);
13746                     _this.tickItems.splice(k, 1);
13747                     
13748                     if(typeof(e) == 'undefined' && view == false){
13749                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13750                     }
13751                     
13752                     rm = true;
13753                     return;
13754                 }
13755             });
13756             
13757             if(rm){
13758                 return;
13759             }
13760             
13761             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13762                 this.tickItems.push(r.data);
13763             }
13764             
13765             if(typeof(e) == 'undefined' && view == false){
13766                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13767             }
13768                     
13769             return;
13770         }
13771         
13772         if(r){
13773             this.onSelect(r, index);
13774         }
13775         if(doFocus !== false && !this.blockFocus){
13776             this.inputEl().focus();
13777         }
13778     },
13779
13780     // private
13781     restrictHeight : function(){
13782         //this.innerList.dom.style.height = '';
13783         //var inner = this.innerList.dom;
13784         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13785         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13786         //this.list.beginUpdate();
13787         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13788         this.list.alignTo(this.inputEl(), this.listAlign);
13789         this.list.alignTo(this.inputEl(), this.listAlign);
13790         //this.list.endUpdate();
13791     },
13792
13793     // private
13794     onEmptyResults : function(){
13795         
13796         if(this.tickable && this.editable){
13797             this.restrictHeight();
13798             return;
13799         }
13800         
13801         this.collapse();
13802     },
13803
13804     /**
13805      * Returns true if the dropdown list is expanded, else false.
13806      */
13807     isExpanded : function(){
13808         return this.list.isVisible();
13809     },
13810
13811     /**
13812      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13813      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13814      * @param {String} value The data value of the item to select
13815      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13816      * selected item if it is not currently in view (defaults to true)
13817      * @return {Boolean} True if the value matched an item in the list, else false
13818      */
13819     selectByValue : function(v, scrollIntoView){
13820         if(v !== undefined && v !== null){
13821             var r = this.findRecord(this.valueField || this.displayField, v);
13822             if(r){
13823                 this.select(this.store.indexOf(r), scrollIntoView);
13824                 return true;
13825             }
13826         }
13827         return false;
13828     },
13829
13830     /**
13831      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13832      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13833      * @param {Number} index The zero-based index of the list item to select
13834      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13835      * selected item if it is not currently in view (defaults to true)
13836      */
13837     select : function(index, scrollIntoView){
13838         this.selectedIndex = index;
13839         this.view.select(index);
13840         if(scrollIntoView !== false){
13841             var el = this.view.getNode(index);
13842             /*
13843              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13844              */
13845             if(el){
13846                 this.list.scrollChildIntoView(el, false);
13847             }
13848         }
13849     },
13850
13851     // private
13852     selectNext : function(){
13853         var ct = this.store.getCount();
13854         if(ct > 0){
13855             if(this.selectedIndex == -1){
13856                 this.select(0);
13857             }else if(this.selectedIndex < ct-1){
13858                 this.select(this.selectedIndex+1);
13859             }
13860         }
13861     },
13862
13863     // private
13864     selectPrev : function(){
13865         var ct = this.store.getCount();
13866         if(ct > 0){
13867             if(this.selectedIndex == -1){
13868                 this.select(0);
13869             }else if(this.selectedIndex != 0){
13870                 this.select(this.selectedIndex-1);
13871             }
13872         }
13873     },
13874
13875     // private
13876     onKeyUp : function(e){
13877         if(this.editable !== false && !e.isSpecialKey()){
13878             this.lastKey = e.getKey();
13879             this.dqTask.delay(this.queryDelay);
13880         }
13881     },
13882
13883     // private
13884     validateBlur : function(){
13885         return !this.list || !this.list.isVisible();   
13886     },
13887
13888     // private
13889     initQuery : function(){
13890         
13891         var v = this.getRawValue();
13892         
13893         if(this.tickable && this.editable){
13894             v = this.tickableInputEl().getValue();
13895         }
13896         
13897         this.doQuery(v);
13898     },
13899
13900     // private
13901     doForce : function(){
13902         if(this.inputEl().dom.value.length > 0){
13903             this.inputEl().dom.value =
13904                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13905              
13906         }
13907     },
13908
13909     /**
13910      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13911      * query allowing the query action to be canceled if needed.
13912      * @param {String} query The SQL query to execute
13913      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13914      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13915      * saved in the current store (defaults to false)
13916      */
13917     doQuery : function(q, forceAll){
13918         
13919         if(q === undefined || q === null){
13920             q = '';
13921         }
13922         var qe = {
13923             query: q,
13924             forceAll: forceAll,
13925             combo: this,
13926             cancel:false
13927         };
13928         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13929             return false;
13930         }
13931         q = qe.query;
13932         
13933         forceAll = qe.forceAll;
13934         if(forceAll === true || (q.length >= this.minChars)){
13935             
13936             this.hasQuery = true;
13937             
13938             if(this.lastQuery != q || this.alwaysQuery){
13939                 this.lastQuery = q;
13940                 if(this.mode == 'local'){
13941                     this.selectedIndex = -1;
13942                     if(forceAll){
13943                         this.store.clearFilter();
13944                     }else{
13945                         
13946                         if(this.specialFilter){
13947                             this.fireEvent('specialfilter', this);
13948                             this.onLoad();
13949                             return;
13950                         }
13951                         
13952                         this.store.filter(this.displayField, q);
13953                     }
13954                     
13955                     this.store.fireEvent("datachanged", this.store);
13956                     
13957                     this.onLoad();
13958                     
13959                     
13960                 }else{
13961                     
13962                     this.store.baseParams[this.queryParam] = q;
13963                     
13964                     var options = {params : this.getParams(q)};
13965                     
13966                     if(this.loadNext){
13967                         options.add = true;
13968                         options.params.start = this.page * this.pageSize;
13969                     }
13970                     
13971                     this.store.load(options);
13972                     
13973                     /*
13974                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13975                      *  we should expand the list on onLoad
13976                      *  so command out it
13977                      */
13978 //                    this.expand();
13979                 }
13980             }else{
13981                 this.selectedIndex = -1;
13982                 this.onLoad();   
13983             }
13984         }
13985         
13986         this.loadNext = false;
13987     },
13988     
13989     // private
13990     getParams : function(q){
13991         var p = {};
13992         //p[this.queryParam] = q;
13993         
13994         if(this.pageSize){
13995             p.start = 0;
13996             p.limit = this.pageSize;
13997         }
13998         return p;
13999     },
14000
14001     /**
14002      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14003      */
14004     collapse : function(){
14005         if(!this.isExpanded()){
14006             return;
14007         }
14008         
14009         this.list.hide();
14010         
14011         this.hasFocus = false;
14012         
14013         if(this.tickable){
14014             this.okBtn.hide();
14015             this.cancelBtn.hide();
14016             this.trigger.show();
14017             
14018             if(this.editable){
14019                 this.tickableInputEl().dom.value = '';
14020                 this.tickableInputEl().blur();
14021             }
14022             
14023         }
14024         
14025         Roo.get(document).un('mousedown', this.collapseIf, this);
14026         Roo.get(document).un('mousewheel', this.collapseIf, this);
14027         if (!this.editable) {
14028             Roo.get(document).un('keydown', this.listKeyPress, this);
14029         }
14030         this.fireEvent('collapse', this);
14031         
14032         this.validate();
14033     },
14034
14035     // private
14036     collapseIf : function(e){
14037         var in_combo  = e.within(this.el);
14038         var in_list =  e.within(this.list);
14039         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14040         
14041         if (in_combo || in_list || is_list) {
14042             //e.stopPropagation();
14043             return;
14044         }
14045         
14046         if(this.tickable){
14047             this.onTickableFooterButtonClick(e, false, false);
14048         }
14049
14050         this.collapse();
14051         
14052     },
14053
14054     /**
14055      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14056      */
14057     expand : function(){
14058        
14059         if(this.isExpanded() || !this.hasFocus){
14060             return;
14061         }
14062         
14063         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14064         this.list.setWidth(lw);
14065         
14066         Roo.log('expand');
14067         
14068         this.list.show();
14069         
14070         this.restrictHeight();
14071         
14072         if(this.tickable){
14073             
14074             this.tickItems = Roo.apply([], this.item);
14075             
14076             this.okBtn.show();
14077             this.cancelBtn.show();
14078             this.trigger.hide();
14079             
14080             if(this.editable){
14081                 this.tickableInputEl().focus();
14082             }
14083             
14084         }
14085         
14086         Roo.get(document).on('mousedown', this.collapseIf, this);
14087         Roo.get(document).on('mousewheel', this.collapseIf, this);
14088         if (!this.editable) {
14089             Roo.get(document).on('keydown', this.listKeyPress, this);
14090         }
14091         
14092         this.fireEvent('expand', this);
14093     },
14094
14095     // private
14096     // Implements the default empty TriggerField.onTriggerClick function
14097     onTriggerClick : function(e)
14098     {
14099         Roo.log('trigger click');
14100         
14101         if(this.disabled || !this.triggerList){
14102             return;
14103         }
14104         
14105         this.page = 0;
14106         this.loadNext = false;
14107         
14108         if(this.isExpanded()){
14109             this.collapse();
14110             if (!this.blockFocus) {
14111                 this.inputEl().focus();
14112             }
14113             
14114         }else {
14115             this.hasFocus = true;
14116             if(this.triggerAction == 'all') {
14117                 this.doQuery(this.allQuery, true);
14118             } else {
14119                 this.doQuery(this.getRawValue());
14120             }
14121             if (!this.blockFocus) {
14122                 this.inputEl().focus();
14123             }
14124         }
14125     },
14126     
14127     onTickableTriggerClick : function(e)
14128     {
14129         if(this.disabled){
14130             return;
14131         }
14132         
14133         this.page = 0;
14134         this.loadNext = false;
14135         this.hasFocus = true;
14136         
14137         if(this.triggerAction == 'all') {
14138             this.doQuery(this.allQuery, true);
14139         } else {
14140             this.doQuery(this.getRawValue());
14141         }
14142     },
14143     
14144     onSearchFieldClick : function(e)
14145     {
14146         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14147             this.onTickableFooterButtonClick(e, false, false);
14148             return;
14149         }
14150         
14151         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14152             return;
14153         }
14154         
14155         this.page = 0;
14156         this.loadNext = false;
14157         this.hasFocus = true;
14158         
14159         if(this.triggerAction == 'all') {
14160             this.doQuery(this.allQuery, true);
14161         } else {
14162             this.doQuery(this.getRawValue());
14163         }
14164     },
14165     
14166     listKeyPress : function(e)
14167     {
14168         //Roo.log('listkeypress');
14169         // scroll to first matching element based on key pres..
14170         if (e.isSpecialKey()) {
14171             return false;
14172         }
14173         var k = String.fromCharCode(e.getKey()).toUpperCase();
14174         //Roo.log(k);
14175         var match  = false;
14176         var csel = this.view.getSelectedNodes();
14177         var cselitem = false;
14178         if (csel.length) {
14179             var ix = this.view.indexOf(csel[0]);
14180             cselitem  = this.store.getAt(ix);
14181             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14182                 cselitem = false;
14183             }
14184             
14185         }
14186         
14187         this.store.each(function(v) { 
14188             if (cselitem) {
14189                 // start at existing selection.
14190                 if (cselitem.id == v.id) {
14191                     cselitem = false;
14192                 }
14193                 return true;
14194             }
14195                 
14196             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14197                 match = this.store.indexOf(v);
14198                 return false;
14199             }
14200             return true;
14201         }, this);
14202         
14203         if (match === false) {
14204             return true; // no more action?
14205         }
14206         // scroll to?
14207         this.view.select(match);
14208         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14209         sn.scrollIntoView(sn.dom.parentNode, false);
14210     },
14211     
14212     onViewScroll : function(e, t){
14213         
14214         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){
14215             return;
14216         }
14217         
14218         this.hasQuery = true;
14219         
14220         this.loading = this.list.select('.loading', true).first();
14221         
14222         if(this.loading === null){
14223             this.list.createChild({
14224                 tag: 'div',
14225                 cls: 'loading roo-select2-more-results roo-select2-active',
14226                 html: 'Loading more results...'
14227             });
14228             
14229             this.loading = this.list.select('.loading', true).first();
14230             
14231             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14232             
14233             this.loading.hide();
14234         }
14235         
14236         this.loading.show();
14237         
14238         var _combo = this;
14239         
14240         this.page++;
14241         this.loadNext = true;
14242         
14243         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14244         
14245         return;
14246     },
14247     
14248     addItem : function(o)
14249     {   
14250         var dv = ''; // display value
14251         
14252         if (this.displayField) {
14253             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14254         } else {
14255             // this is an error condition!!!
14256             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14257         }
14258         
14259         if(!dv.length){
14260             return;
14261         }
14262         
14263         var choice = this.choices.createChild({
14264             tag: 'li',
14265             cls: 'roo-select2-search-choice',
14266             cn: [
14267                 {
14268                     tag: 'div',
14269                     html: dv
14270                 },
14271                 {
14272                     tag: 'a',
14273                     href: '#',
14274                     cls: 'roo-select2-search-choice-close',
14275                     tabindex: '-1'
14276                 }
14277             ]
14278             
14279         }, this.searchField);
14280         
14281         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14282         
14283         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14284         
14285         this.item.push(o);
14286         
14287         this.lastData = o;
14288         
14289         this.syncValue();
14290         
14291         this.inputEl().dom.value = '';
14292         
14293         this.validate();
14294     },
14295     
14296     onRemoveItem : function(e, _self, o)
14297     {
14298         e.preventDefault();
14299         
14300         this.lastItem = Roo.apply([], this.item);
14301         
14302         var index = this.item.indexOf(o.data) * 1;
14303         
14304         if( index < 0){
14305             Roo.log('not this item?!');
14306             return;
14307         }
14308         
14309         this.item.splice(index, 1);
14310         o.item.remove();
14311         
14312         this.syncValue();
14313         
14314         this.fireEvent('remove', this, e);
14315         
14316         this.validate();
14317         
14318     },
14319     
14320     syncValue : function()
14321     {
14322         if(!this.item.length){
14323             this.clearValue();
14324             return;
14325         }
14326             
14327         var value = [];
14328         var _this = this;
14329         Roo.each(this.item, function(i){
14330             if(_this.valueField){
14331                 value.push(i[_this.valueField]);
14332                 return;
14333             }
14334
14335             value.push(i);
14336         });
14337
14338         this.value = value.join(',');
14339
14340         if(this.hiddenField){
14341             this.hiddenField.dom.value = this.value;
14342         }
14343         
14344         this.store.fireEvent("datachanged", this.store);
14345         
14346         this.validate();
14347     },
14348     
14349     clearItem : function()
14350     {
14351         if(!this.multiple){
14352             return;
14353         }
14354         
14355         this.item = [];
14356         
14357         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14358            c.remove();
14359         });
14360         
14361         this.syncValue();
14362         
14363         this.validate();
14364         
14365         if(this.tickable && !Roo.isTouch){
14366             this.view.refresh();
14367         }
14368     },
14369     
14370     inputEl: function ()
14371     {
14372         if(Roo.isIOS && this.useNativeIOS){
14373             return this.el.select('select.roo-ios-select', true).first();
14374         }
14375         
14376         if(Roo.isTouch && this.mobileTouchView){
14377             return this.el.select('input.form-control',true).first();
14378         }
14379         
14380         if(this.tickable){
14381             return this.searchField;
14382         }
14383         
14384         return this.el.select('input.form-control',true).first();
14385     },
14386     
14387     onTickableFooterButtonClick : function(e, btn, el)
14388     {
14389         e.preventDefault();
14390         
14391         this.lastItem = Roo.apply([], this.item);
14392         
14393         if(btn && btn.name == 'cancel'){
14394             this.tickItems = Roo.apply([], this.item);
14395             this.collapse();
14396             return;
14397         }
14398         
14399         this.clearItem();
14400         
14401         var _this = this;
14402         
14403         Roo.each(this.tickItems, function(o){
14404             _this.addItem(o);
14405         });
14406         
14407         this.collapse();
14408         
14409     },
14410     
14411     validate : function()
14412     {
14413         var v = this.getRawValue();
14414         
14415         if(this.multiple){
14416             v = this.getValue();
14417         }
14418         
14419         if(this.disabled || this.allowBlank || v.length){
14420             this.markValid();
14421             return true;
14422         }
14423         
14424         this.markInvalid();
14425         return false;
14426     },
14427     
14428     tickableInputEl : function()
14429     {
14430         if(!this.tickable || !this.editable){
14431             return this.inputEl();
14432         }
14433         
14434         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14435     },
14436     
14437     
14438     getAutoCreateTouchView : function()
14439     {
14440         var id = Roo.id();
14441         
14442         var cfg = {
14443             cls: 'form-group' //input-group
14444         };
14445         
14446         var input =  {
14447             tag: 'input',
14448             id : id,
14449             type : this.inputType,
14450             cls : 'form-control x-combo-noedit',
14451             autocomplete: 'new-password',
14452             placeholder : this.placeholder || '',
14453             readonly : true
14454         };
14455         
14456         if (this.name) {
14457             input.name = this.name;
14458         }
14459         
14460         if (this.size) {
14461             input.cls += ' input-' + this.size;
14462         }
14463         
14464         if (this.disabled) {
14465             input.disabled = true;
14466         }
14467         
14468         var inputblock = {
14469             cls : '',
14470             cn : [
14471                 input
14472             ]
14473         };
14474         
14475         if(this.before){
14476             inputblock.cls += ' input-group';
14477             
14478             inputblock.cn.unshift({
14479                 tag :'span',
14480                 cls : 'input-group-addon',
14481                 html : this.before
14482             });
14483         }
14484         
14485         if(this.removable && !this.multiple){
14486             inputblock.cls += ' roo-removable';
14487             
14488             inputblock.cn.push({
14489                 tag: 'button',
14490                 html : 'x',
14491                 cls : 'roo-combo-removable-btn close'
14492             });
14493         }
14494
14495         if(this.hasFeedback && !this.allowBlank){
14496             
14497             inputblock.cls += ' has-feedback';
14498             
14499             inputblock.cn.push({
14500                 tag: 'span',
14501                 cls: 'glyphicon form-control-feedback'
14502             });
14503             
14504         }
14505         
14506         if (this.after) {
14507             
14508             inputblock.cls += (this.before) ? '' : ' input-group';
14509             
14510             inputblock.cn.push({
14511                 tag :'span',
14512                 cls : 'input-group-addon',
14513                 html : this.after
14514             });
14515         }
14516
14517         var box = {
14518             tag: 'div',
14519             cn: [
14520                 {
14521                     tag: 'input',
14522                     type : 'hidden',
14523                     cls: 'form-hidden-field'
14524                 },
14525                 inputblock
14526             ]
14527             
14528         };
14529         
14530         if(this.multiple){
14531             box = {
14532                 tag: 'div',
14533                 cn: [
14534                     {
14535                         tag: 'input',
14536                         type : 'hidden',
14537                         cls: 'form-hidden-field'
14538                     },
14539                     {
14540                         tag: 'ul',
14541                         cls: 'roo-select2-choices',
14542                         cn:[
14543                             {
14544                                 tag: 'li',
14545                                 cls: 'roo-select2-search-field',
14546                                 cn: [
14547
14548                                     inputblock
14549                                 ]
14550                             }
14551                         ]
14552                     }
14553                 ]
14554             }
14555         };
14556         
14557         var combobox = {
14558             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14559             cn: [
14560                 box
14561             ]
14562         };
14563         
14564         if(!this.multiple && this.showToggleBtn){
14565             
14566             var caret = {
14567                         tag: 'span',
14568                         cls: 'caret'
14569             };
14570             
14571             if (this.caret != false) {
14572                 caret = {
14573                      tag: 'i',
14574                      cls: 'fa fa-' + this.caret
14575                 };
14576                 
14577             }
14578             
14579             combobox.cn.push({
14580                 tag :'span',
14581                 cls : 'input-group-addon btn dropdown-toggle',
14582                 cn : [
14583                     caret,
14584                     {
14585                         tag: 'span',
14586                         cls: 'combobox-clear',
14587                         cn  : [
14588                             {
14589                                 tag : 'i',
14590                                 cls: 'icon-remove'
14591                             }
14592                         ]
14593                     }
14594                 ]
14595
14596             })
14597         }
14598         
14599         if(this.multiple){
14600             combobox.cls += ' roo-select2-container-multi';
14601         }
14602         
14603         var align = this.labelAlign || this.parentLabelAlign();
14604         
14605         if (align ==='left' && this.fieldLabel.length) {
14606
14607             cfg.cn = [
14608                 {
14609                    tag : 'i',
14610                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14611                    tooltip : 'This field is required'
14612                 },
14613                 {
14614                     tag: 'label',
14615                     cls : 'control-label',
14616                     html : this.fieldLabel
14617
14618                 },
14619                 {
14620                     cls : '', 
14621                     cn: [
14622                         combobox
14623                     ]
14624                 }
14625             ];
14626             
14627             var labelCfg = cfg.cn[1];
14628             var contentCfg = cfg.cn[2];
14629             
14630
14631             if(this.indicatorpos == 'right'){
14632                 cfg.cn = [
14633                     {
14634                         tag: 'label',
14635                         cls : 'control-label',
14636                         html : this.fieldLabel,
14637                         cn : [
14638                             {
14639                                tag : 'i',
14640                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14641                                tooltip : 'This field is required'
14642                             }
14643                         ]
14644                     },
14645                     {
14646                         cls : '', 
14647                         cn: [
14648                             combobox
14649                         ]
14650                     }
14651                 ];
14652             }
14653             
14654             labelCfg = cfg.cn[0];
14655             contentCfg = cfg.cn[2];
14656             
14657             if(this.labelWidth > 12){
14658                 labelCfg.style = "width: " + this.labelWidth + 'px';
14659             }
14660             
14661             if(this.labelWidth < 13 && this.labelmd == 0){
14662                 this.labelmd = this.labelWidth;
14663             }
14664             
14665             if(this.labellg > 0){
14666                 labelCfg.cls += ' col-lg-' + this.labellg;
14667                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14668             }
14669             
14670             if(this.labelmd > 0){
14671                 labelCfg.cls += ' col-md-' + this.labelmd;
14672                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14673             }
14674             
14675             if(this.labelsm > 0){
14676                 labelCfg.cls += ' col-sm-' + this.labelsm;
14677                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14678             }
14679             
14680             if(this.labelxs > 0){
14681                 labelCfg.cls += ' col-xs-' + this.labelxs;
14682                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14683             }
14684                 
14685                 
14686         } else if ( this.fieldLabel.length) {
14687             cfg.cn = [
14688                 {
14689                    tag : 'i',
14690                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14691                    tooltip : 'This field is required'
14692                 },
14693                 {
14694                     tag: 'label',
14695                     cls : 'control-label',
14696                     html : this.fieldLabel
14697
14698                 },
14699                 {
14700                     cls : '', 
14701                     cn: [
14702                         combobox
14703                     ]
14704                 }
14705             ];
14706             
14707             if(this.indicatorpos == 'right'){
14708                 cfg.cn = [
14709                     {
14710                         tag: 'label',
14711                         cls : 'control-label',
14712                         html : this.fieldLabel,
14713                         cn : [
14714                             {
14715                                tag : 'i',
14716                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14717                                tooltip : 'This field is required'
14718                             }
14719                         ]
14720                     },
14721                     {
14722                         cls : '', 
14723                         cn: [
14724                             combobox
14725                         ]
14726                     }
14727                 ];
14728             }
14729         } else {
14730             cfg.cn = combobox;    
14731         }
14732         
14733         
14734         var settings = this;
14735         
14736         ['xs','sm','md','lg'].map(function(size){
14737             if (settings[size]) {
14738                 cfg.cls += ' col-' + size + '-' + settings[size];
14739             }
14740         });
14741         
14742         return cfg;
14743     },
14744     
14745     initTouchView : function()
14746     {
14747         this.renderTouchView();
14748         
14749         this.touchViewEl.on('scroll', function(){
14750             this.el.dom.scrollTop = 0;
14751         }, this);
14752         
14753         this.originalValue = this.getValue();
14754         
14755         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14756         
14757         this.inputEl().on("click", this.showTouchView, this);
14758         if (this.triggerEl) {
14759             this.triggerEl.on("click", this.showTouchView, this);
14760         }
14761         
14762         
14763         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14764         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14765         
14766         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14767         
14768         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14769         this.store.on('load', this.onTouchViewLoad, this);
14770         this.store.on('loadexception', this.onTouchViewLoadException, this);
14771         
14772         if(this.hiddenName){
14773             
14774             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14775             
14776             this.hiddenField.dom.value =
14777                 this.hiddenValue !== undefined ? this.hiddenValue :
14778                 this.value !== undefined ? this.value : '';
14779         
14780             this.el.dom.removeAttribute('name');
14781             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14782         }
14783         
14784         if(this.multiple){
14785             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14786             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14787         }
14788         
14789         if(this.removable && !this.multiple){
14790             var close = this.closeTriggerEl();
14791             if(close){
14792                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14793                 close.on('click', this.removeBtnClick, this, close);
14794             }
14795         }
14796         /*
14797          * fix the bug in Safari iOS8
14798          */
14799         this.inputEl().on("focus", function(e){
14800             document.activeElement.blur();
14801         }, this);
14802         
14803         return;
14804         
14805         
14806     },
14807     
14808     renderTouchView : function()
14809     {
14810         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14811         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14812         
14813         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14814         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14815         
14816         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14817         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14818         this.touchViewBodyEl.setStyle('overflow', 'auto');
14819         
14820         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14821         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14822         
14823         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14824         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14825         
14826     },
14827     
14828     showTouchView : function()
14829     {
14830         if(this.disabled){
14831             return;
14832         }
14833         
14834         this.touchViewHeaderEl.hide();
14835
14836         if(this.modalTitle.length){
14837             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14838             this.touchViewHeaderEl.show();
14839         }
14840
14841         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14842         this.touchViewEl.show();
14843
14844         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14845         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14846                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14847
14848         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14849
14850         if(this.modalTitle.length){
14851             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14852         }
14853         
14854         this.touchViewBodyEl.setHeight(bodyHeight);
14855
14856         if(this.animate){
14857             var _this = this;
14858             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14859         }else{
14860             this.touchViewEl.addClass('in');
14861         }
14862
14863         this.doTouchViewQuery();
14864         
14865     },
14866     
14867     hideTouchView : function()
14868     {
14869         this.touchViewEl.removeClass('in');
14870
14871         if(this.animate){
14872             var _this = this;
14873             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14874         }else{
14875             this.touchViewEl.setStyle('display', 'none');
14876         }
14877         
14878     },
14879     
14880     setTouchViewValue : function()
14881     {
14882         if(this.multiple){
14883             this.clearItem();
14884         
14885             var _this = this;
14886
14887             Roo.each(this.tickItems, function(o){
14888                 this.addItem(o);
14889             }, this);
14890         }
14891         
14892         this.hideTouchView();
14893     },
14894     
14895     doTouchViewQuery : function()
14896     {
14897         var qe = {
14898             query: '',
14899             forceAll: true,
14900             combo: this,
14901             cancel:false
14902         };
14903         
14904         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14905             return false;
14906         }
14907         
14908         if(!this.alwaysQuery || this.mode == 'local'){
14909             this.onTouchViewLoad();
14910             return;
14911         }
14912         
14913         this.store.load();
14914     },
14915     
14916     onTouchViewBeforeLoad : function(combo,opts)
14917     {
14918         return;
14919     },
14920
14921     // private
14922     onTouchViewLoad : function()
14923     {
14924         if(this.store.getCount() < 1){
14925             this.onTouchViewEmptyResults();
14926             return;
14927         }
14928         
14929         this.clearTouchView();
14930         
14931         var rawValue = this.getRawValue();
14932         
14933         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14934         
14935         this.tickItems = [];
14936         
14937         this.store.data.each(function(d, rowIndex){
14938             var row = this.touchViewListGroup.createChild(template);
14939             
14940             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14941                 row.addClass(d.data.cls);
14942             }
14943             
14944             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14945                 var cfg = {
14946                     data : d.data,
14947                     html : d.data[this.displayField]
14948                 };
14949                 
14950                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14951                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14952                 }
14953             }
14954             row.removeClass('selected');
14955             if(!this.multiple && this.valueField &&
14956                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14957             {
14958                 // radio buttons..
14959                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14960                 row.addClass('selected');
14961             }
14962             
14963             if(this.multiple && this.valueField &&
14964                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14965             {
14966                 
14967                 // checkboxes...
14968                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14969                 this.tickItems.push(d.data);
14970             }
14971             
14972             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14973             
14974         }, this);
14975         
14976         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14977         
14978         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14979
14980         if(this.modalTitle.length){
14981             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14982         }
14983
14984         var listHeight = this.touchViewListGroup.getHeight();
14985         
14986         var _this = this;
14987         
14988         if(firstChecked && listHeight > bodyHeight){
14989             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14990         }
14991         
14992     },
14993     
14994     onTouchViewLoadException : function()
14995     {
14996         this.hideTouchView();
14997     },
14998     
14999     onTouchViewEmptyResults : function()
15000     {
15001         this.clearTouchView();
15002         
15003         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15004         
15005         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15006         
15007     },
15008     
15009     clearTouchView : function()
15010     {
15011         this.touchViewListGroup.dom.innerHTML = '';
15012     },
15013     
15014     onTouchViewClick : function(e, el, o)
15015     {
15016         e.preventDefault();
15017         
15018         var row = o.row;
15019         var rowIndex = o.rowIndex;
15020         
15021         var r = this.store.getAt(rowIndex);
15022         
15023         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15024             
15025             if(!this.multiple){
15026                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15027                     c.dom.removeAttribute('checked');
15028                 }, this);
15029
15030                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15031
15032                 this.setFromData(r.data);
15033
15034                 var close = this.closeTriggerEl();
15035
15036                 if(close){
15037                     close.show();
15038                 }
15039
15040                 this.hideTouchView();
15041
15042                 this.fireEvent('select', this, r, rowIndex);
15043
15044                 return;
15045             }
15046
15047             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15048                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15049                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15050                 return;
15051             }
15052
15053             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15054             this.addItem(r.data);
15055             this.tickItems.push(r.data);
15056         }
15057     },
15058     
15059     getAutoCreateNativeIOS : function()
15060     {
15061         var cfg = {
15062             cls: 'form-group' //input-group,
15063         };
15064         
15065         var combobox =  {
15066             tag: 'select',
15067             cls : 'roo-ios-select'
15068         };
15069         
15070         if (this.name) {
15071             combobox.name = this.name;
15072         }
15073         
15074         if (this.disabled) {
15075             combobox.disabled = true;
15076         }
15077         
15078         var settings = this;
15079         
15080         ['xs','sm','md','lg'].map(function(size){
15081             if (settings[size]) {
15082                 cfg.cls += ' col-' + size + '-' + settings[size];
15083             }
15084         });
15085         
15086         cfg.cn = combobox;
15087         
15088         return cfg;
15089         
15090     },
15091     
15092     initIOSView : function()
15093     {
15094         this.store.on('load', this.onIOSViewLoad, this);
15095         
15096         return;
15097     },
15098     
15099     onIOSViewLoad : function()
15100     {
15101         if(this.store.getCount() < 1){
15102             return;
15103         }
15104         
15105         this.clearIOSView();
15106         
15107         if(this.allowBlank) {
15108             
15109             var default_text = '-- SELECT --';
15110             
15111             var opt = this.inputEl().createChild({
15112                 tag: 'option',
15113                 value : 0,
15114                 html : default_text
15115             });
15116             
15117             var o = {};
15118             o[this.valueField] = 0;
15119             o[this.displayField] = default_text;
15120             
15121             this.ios_options.push({
15122                 data : o,
15123                 el : opt
15124             });
15125             
15126         }
15127         
15128         this.store.data.each(function(d, rowIndex){
15129             
15130             var html = '';
15131             
15132             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15133                 html = d.data[this.displayField];
15134             }
15135             
15136             var value = '';
15137             
15138             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15139                 value = d.data[this.valueField];
15140             }
15141             
15142             var option = {
15143                 tag: 'option',
15144                 value : value,
15145                 html : html
15146             };
15147             
15148             if(this.value == d.data[this.valueField]){
15149                 option['selected'] = true;
15150             }
15151             
15152             var opt = this.inputEl().createChild(option);
15153             
15154             this.ios_options.push({
15155                 data : d.data,
15156                 el : opt
15157             });
15158             
15159         }, this);
15160         
15161         this.inputEl().on('change', function(){
15162            this.fireEvent('select', this);
15163         }, this);
15164         
15165     },
15166     
15167     clearIOSView: function()
15168     {
15169         this.inputEl().dom.innerHTML = '';
15170         
15171         this.ios_options = [];
15172     },
15173     
15174     setIOSValue: function(v)
15175     {
15176         this.value = v;
15177         
15178         if(!this.ios_options){
15179             return;
15180         }
15181         
15182         Roo.each(this.ios_options, function(opts){
15183            
15184            opts.el.dom.removeAttribute('selected');
15185            
15186            if(opts.data[this.valueField] != v){
15187                return;
15188            }
15189            
15190            opts.el.dom.setAttribute('selected', true);
15191            
15192         }, this);
15193     }
15194
15195     /** 
15196     * @cfg {Boolean} grow 
15197     * @hide 
15198     */
15199     /** 
15200     * @cfg {Number} growMin 
15201     * @hide 
15202     */
15203     /** 
15204     * @cfg {Number} growMax 
15205     * @hide 
15206     */
15207     /**
15208      * @hide
15209      * @method autoSize
15210      */
15211 });
15212
15213 Roo.apply(Roo.bootstrap.ComboBox,  {
15214     
15215     header : {
15216         tag: 'div',
15217         cls: 'modal-header',
15218         cn: [
15219             {
15220                 tag: 'h4',
15221                 cls: 'modal-title'
15222             }
15223         ]
15224     },
15225     
15226     body : {
15227         tag: 'div',
15228         cls: 'modal-body',
15229         cn: [
15230             {
15231                 tag: 'ul',
15232                 cls: 'list-group'
15233             }
15234         ]
15235     },
15236     
15237     listItemRadio : {
15238         tag: 'li',
15239         cls: 'list-group-item',
15240         cn: [
15241             {
15242                 tag: 'span',
15243                 cls: 'roo-combobox-list-group-item-value'
15244             },
15245             {
15246                 tag: 'div',
15247                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15248                 cn: [
15249                     {
15250                         tag: 'input',
15251                         type: 'radio'
15252                     },
15253                     {
15254                         tag: 'label'
15255                     }
15256                 ]
15257             }
15258         ]
15259     },
15260     
15261     listItemCheckbox : {
15262         tag: 'li',
15263         cls: 'list-group-item',
15264         cn: [
15265             {
15266                 tag: 'span',
15267                 cls: 'roo-combobox-list-group-item-value'
15268             },
15269             {
15270                 tag: 'div',
15271                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15272                 cn: [
15273                     {
15274                         tag: 'input',
15275                         type: 'checkbox'
15276                     },
15277                     {
15278                         tag: 'label'
15279                     }
15280                 ]
15281             }
15282         ]
15283     },
15284     
15285     emptyResult : {
15286         tag: 'div',
15287         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15288     },
15289     
15290     footer : {
15291         tag: 'div',
15292         cls: 'modal-footer',
15293         cn: [
15294             {
15295                 tag: 'div',
15296                 cls: 'row',
15297                 cn: [
15298                     {
15299                         tag: 'div',
15300                         cls: 'col-xs-6 text-left',
15301                         cn: {
15302                             tag: 'button',
15303                             cls: 'btn btn-danger roo-touch-view-cancel',
15304                             html: 'Cancel'
15305                         }
15306                     },
15307                     {
15308                         tag: 'div',
15309                         cls: 'col-xs-6 text-right',
15310                         cn: {
15311                             tag: 'button',
15312                             cls: 'btn btn-success roo-touch-view-ok',
15313                             html: 'OK'
15314                         }
15315                     }
15316                 ]
15317             }
15318         ]
15319         
15320     }
15321 });
15322
15323 Roo.apply(Roo.bootstrap.ComboBox,  {
15324     
15325     touchViewTemplate : {
15326         tag: 'div',
15327         cls: 'modal fade roo-combobox-touch-view',
15328         cn: [
15329             {
15330                 tag: 'div',
15331                 cls: 'modal-dialog',
15332                 style : 'position:fixed', // we have to fix position....
15333                 cn: [
15334                     {
15335                         tag: 'div',
15336                         cls: 'modal-content',
15337                         cn: [
15338                             Roo.bootstrap.ComboBox.header,
15339                             Roo.bootstrap.ComboBox.body,
15340                             Roo.bootstrap.ComboBox.footer
15341                         ]
15342                     }
15343                 ]
15344             }
15345         ]
15346     }
15347 });/*
15348  * Based on:
15349  * Ext JS Library 1.1.1
15350  * Copyright(c) 2006-2007, Ext JS, LLC.
15351  *
15352  * Originally Released Under LGPL - original licence link has changed is not relivant.
15353  *
15354  * Fork - LGPL
15355  * <script type="text/javascript">
15356  */
15357
15358 /**
15359  * @class Roo.View
15360  * @extends Roo.util.Observable
15361  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15362  * This class also supports single and multi selection modes. <br>
15363  * Create a data model bound view:
15364  <pre><code>
15365  var store = new Roo.data.Store(...);
15366
15367  var view = new Roo.View({
15368     el : "my-element",
15369     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15370  
15371     singleSelect: true,
15372     selectedClass: "ydataview-selected",
15373     store: store
15374  });
15375
15376  // listen for node click?
15377  view.on("click", function(vw, index, node, e){
15378  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15379  });
15380
15381  // load XML data
15382  dataModel.load("foobar.xml");
15383  </code></pre>
15384  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15385  * <br><br>
15386  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15387  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15388  * 
15389  * Note: old style constructor is still suported (container, template, config)
15390  * 
15391  * @constructor
15392  * Create a new View
15393  * @param {Object} config The config object
15394  * 
15395  */
15396 Roo.View = function(config, depreciated_tpl, depreciated_config){
15397     
15398     this.parent = false;
15399     
15400     if (typeof(depreciated_tpl) == 'undefined') {
15401         // new way.. - universal constructor.
15402         Roo.apply(this, config);
15403         this.el  = Roo.get(this.el);
15404     } else {
15405         // old format..
15406         this.el  = Roo.get(config);
15407         this.tpl = depreciated_tpl;
15408         Roo.apply(this, depreciated_config);
15409     }
15410     this.wrapEl  = this.el.wrap().wrap();
15411     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15412     
15413     
15414     if(typeof(this.tpl) == "string"){
15415         this.tpl = new Roo.Template(this.tpl);
15416     } else {
15417         // support xtype ctors..
15418         this.tpl = new Roo.factory(this.tpl, Roo);
15419     }
15420     
15421     
15422     this.tpl.compile();
15423     
15424     /** @private */
15425     this.addEvents({
15426         /**
15427          * @event beforeclick
15428          * Fires before a click is processed. Returns false to cancel the default action.
15429          * @param {Roo.View} this
15430          * @param {Number} index The index of the target node
15431          * @param {HTMLElement} node The target node
15432          * @param {Roo.EventObject} e The raw event object
15433          */
15434             "beforeclick" : true,
15435         /**
15436          * @event click
15437          * Fires when a template node is clicked.
15438          * @param {Roo.View} this
15439          * @param {Number} index The index of the target node
15440          * @param {HTMLElement} node The target node
15441          * @param {Roo.EventObject} e The raw event object
15442          */
15443             "click" : true,
15444         /**
15445          * @event dblclick
15446          * Fires when a template node is double clicked.
15447          * @param {Roo.View} this
15448          * @param {Number} index The index of the target node
15449          * @param {HTMLElement} node The target node
15450          * @param {Roo.EventObject} e The raw event object
15451          */
15452             "dblclick" : true,
15453         /**
15454          * @event contextmenu
15455          * Fires when a template node is right clicked.
15456          * @param {Roo.View} this
15457          * @param {Number} index The index of the target node
15458          * @param {HTMLElement} node The target node
15459          * @param {Roo.EventObject} e The raw event object
15460          */
15461             "contextmenu" : true,
15462         /**
15463          * @event selectionchange
15464          * Fires when the selected nodes change.
15465          * @param {Roo.View} this
15466          * @param {Array} selections Array of the selected nodes
15467          */
15468             "selectionchange" : true,
15469     
15470         /**
15471          * @event beforeselect
15472          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15473          * @param {Roo.View} this
15474          * @param {HTMLElement} node The node to be selected
15475          * @param {Array} selections Array of currently selected nodes
15476          */
15477             "beforeselect" : true,
15478         /**
15479          * @event preparedata
15480          * Fires on every row to render, to allow you to change the data.
15481          * @param {Roo.View} this
15482          * @param {Object} data to be rendered (change this)
15483          */
15484           "preparedata" : true
15485           
15486           
15487         });
15488
15489
15490
15491     this.el.on({
15492         "click": this.onClick,
15493         "dblclick": this.onDblClick,
15494         "contextmenu": this.onContextMenu,
15495         scope:this
15496     });
15497
15498     this.selections = [];
15499     this.nodes = [];
15500     this.cmp = new Roo.CompositeElementLite([]);
15501     if(this.store){
15502         this.store = Roo.factory(this.store, Roo.data);
15503         this.setStore(this.store, true);
15504     }
15505     
15506     if ( this.footer && this.footer.xtype) {
15507            
15508          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15509         
15510         this.footer.dataSource = this.store;
15511         this.footer.container = fctr;
15512         this.footer = Roo.factory(this.footer, Roo);
15513         fctr.insertFirst(this.el);
15514         
15515         // this is a bit insane - as the paging toolbar seems to detach the el..
15516 //        dom.parentNode.parentNode.parentNode
15517          // they get detached?
15518     }
15519     
15520     
15521     Roo.View.superclass.constructor.call(this);
15522     
15523     
15524 };
15525
15526 Roo.extend(Roo.View, Roo.util.Observable, {
15527     
15528      /**
15529      * @cfg {Roo.data.Store} store Data store to load data from.
15530      */
15531     store : false,
15532     
15533     /**
15534      * @cfg {String|Roo.Element} el The container element.
15535      */
15536     el : '',
15537     
15538     /**
15539      * @cfg {String|Roo.Template} tpl The template used by this View 
15540      */
15541     tpl : false,
15542     /**
15543      * @cfg {String} dataName the named area of the template to use as the data area
15544      *                          Works with domtemplates roo-name="name"
15545      */
15546     dataName: false,
15547     /**
15548      * @cfg {String} selectedClass The css class to add to selected nodes
15549      */
15550     selectedClass : "x-view-selected",
15551      /**
15552      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15553      */
15554     emptyText : "",
15555     
15556     /**
15557      * @cfg {String} text to display on mask (default Loading)
15558      */
15559     mask : false,
15560     /**
15561      * @cfg {Boolean} multiSelect Allow multiple selection
15562      */
15563     multiSelect : false,
15564     /**
15565      * @cfg {Boolean} singleSelect Allow single selection
15566      */
15567     singleSelect:  false,
15568     
15569     /**
15570      * @cfg {Boolean} toggleSelect - selecting 
15571      */
15572     toggleSelect : false,
15573     
15574     /**
15575      * @cfg {Boolean} tickable - selecting 
15576      */
15577     tickable : false,
15578     
15579     /**
15580      * Returns the element this view is bound to.
15581      * @return {Roo.Element}
15582      */
15583     getEl : function(){
15584         return this.wrapEl;
15585     },
15586     
15587     
15588
15589     /**
15590      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15591      */
15592     refresh : function(){
15593         //Roo.log('refresh');
15594         var t = this.tpl;
15595         
15596         // if we are using something like 'domtemplate', then
15597         // the what gets used is:
15598         // t.applySubtemplate(NAME, data, wrapping data..)
15599         // the outer template then get' applied with
15600         //     the store 'extra data'
15601         // and the body get's added to the
15602         //      roo-name="data" node?
15603         //      <span class='roo-tpl-{name}'></span> ?????
15604         
15605         
15606         
15607         this.clearSelections();
15608         this.el.update("");
15609         var html = [];
15610         var records = this.store.getRange();
15611         if(records.length < 1) {
15612             
15613             // is this valid??  = should it render a template??
15614             
15615             this.el.update(this.emptyText);
15616             return;
15617         }
15618         var el = this.el;
15619         if (this.dataName) {
15620             this.el.update(t.apply(this.store.meta)); //????
15621             el = this.el.child('.roo-tpl-' + this.dataName);
15622         }
15623         
15624         for(var i = 0, len = records.length; i < len; i++){
15625             var data = this.prepareData(records[i].data, i, records[i]);
15626             this.fireEvent("preparedata", this, data, i, records[i]);
15627             
15628             var d = Roo.apply({}, data);
15629             
15630             if(this.tickable){
15631                 Roo.apply(d, {'roo-id' : Roo.id()});
15632                 
15633                 var _this = this;
15634             
15635                 Roo.each(this.parent.item, function(item){
15636                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15637                         return;
15638                     }
15639                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15640                 });
15641             }
15642             
15643             html[html.length] = Roo.util.Format.trim(
15644                 this.dataName ?
15645                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15646                     t.apply(d)
15647             );
15648         }
15649         
15650         
15651         
15652         el.update(html.join(""));
15653         this.nodes = el.dom.childNodes;
15654         this.updateIndexes(0);
15655     },
15656     
15657
15658     /**
15659      * Function to override to reformat the data that is sent to
15660      * the template for each node.
15661      * DEPRICATED - use the preparedata event handler.
15662      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15663      * a JSON object for an UpdateManager bound view).
15664      */
15665     prepareData : function(data, index, record)
15666     {
15667         this.fireEvent("preparedata", this, data, index, record);
15668         return data;
15669     },
15670
15671     onUpdate : function(ds, record){
15672         // Roo.log('on update');   
15673         this.clearSelections();
15674         var index = this.store.indexOf(record);
15675         var n = this.nodes[index];
15676         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15677         n.parentNode.removeChild(n);
15678         this.updateIndexes(index, index);
15679     },
15680
15681     
15682     
15683 // --------- FIXME     
15684     onAdd : function(ds, records, index)
15685     {
15686         //Roo.log(['on Add', ds, records, index] );        
15687         this.clearSelections();
15688         if(this.nodes.length == 0){
15689             this.refresh();
15690             return;
15691         }
15692         var n = this.nodes[index];
15693         for(var i = 0, len = records.length; i < len; i++){
15694             var d = this.prepareData(records[i].data, i, records[i]);
15695             if(n){
15696                 this.tpl.insertBefore(n, d);
15697             }else{
15698                 
15699                 this.tpl.append(this.el, d);
15700             }
15701         }
15702         this.updateIndexes(index);
15703     },
15704
15705     onRemove : function(ds, record, index){
15706        // Roo.log('onRemove');
15707         this.clearSelections();
15708         var el = this.dataName  ?
15709             this.el.child('.roo-tpl-' + this.dataName) :
15710             this.el; 
15711         
15712         el.dom.removeChild(this.nodes[index]);
15713         this.updateIndexes(index);
15714     },
15715
15716     /**
15717      * Refresh an individual node.
15718      * @param {Number} index
15719      */
15720     refreshNode : function(index){
15721         this.onUpdate(this.store, this.store.getAt(index));
15722     },
15723
15724     updateIndexes : function(startIndex, endIndex){
15725         var ns = this.nodes;
15726         startIndex = startIndex || 0;
15727         endIndex = endIndex || ns.length - 1;
15728         for(var i = startIndex; i <= endIndex; i++){
15729             ns[i].nodeIndex = i;
15730         }
15731     },
15732
15733     /**
15734      * Changes the data store this view uses and refresh the view.
15735      * @param {Store} store
15736      */
15737     setStore : function(store, initial){
15738         if(!initial && this.store){
15739             this.store.un("datachanged", this.refresh);
15740             this.store.un("add", this.onAdd);
15741             this.store.un("remove", this.onRemove);
15742             this.store.un("update", this.onUpdate);
15743             this.store.un("clear", this.refresh);
15744             this.store.un("beforeload", this.onBeforeLoad);
15745             this.store.un("load", this.onLoad);
15746             this.store.un("loadexception", this.onLoad);
15747         }
15748         if(store){
15749           
15750             store.on("datachanged", this.refresh, this);
15751             store.on("add", this.onAdd, this);
15752             store.on("remove", this.onRemove, this);
15753             store.on("update", this.onUpdate, this);
15754             store.on("clear", this.refresh, this);
15755             store.on("beforeload", this.onBeforeLoad, this);
15756             store.on("load", this.onLoad, this);
15757             store.on("loadexception", this.onLoad, this);
15758         }
15759         
15760         if(store){
15761             this.refresh();
15762         }
15763     },
15764     /**
15765      * onbeforeLoad - masks the loading area.
15766      *
15767      */
15768     onBeforeLoad : function(store,opts)
15769     {
15770          //Roo.log('onBeforeLoad');   
15771         if (!opts.add) {
15772             this.el.update("");
15773         }
15774         this.el.mask(this.mask ? this.mask : "Loading" ); 
15775     },
15776     onLoad : function ()
15777     {
15778         this.el.unmask();
15779     },
15780     
15781
15782     /**
15783      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15784      * @param {HTMLElement} node
15785      * @return {HTMLElement} The template node
15786      */
15787     findItemFromChild : function(node){
15788         var el = this.dataName  ?
15789             this.el.child('.roo-tpl-' + this.dataName,true) :
15790             this.el.dom; 
15791         
15792         if(!node || node.parentNode == el){
15793                     return node;
15794             }
15795             var p = node.parentNode;
15796             while(p && p != el){
15797             if(p.parentNode == el){
15798                 return p;
15799             }
15800             p = p.parentNode;
15801         }
15802             return null;
15803     },
15804
15805     /** @ignore */
15806     onClick : function(e){
15807         var item = this.findItemFromChild(e.getTarget());
15808         if(item){
15809             var index = this.indexOf(item);
15810             if(this.onItemClick(item, index, e) !== false){
15811                 this.fireEvent("click", this, index, item, e);
15812             }
15813         }else{
15814             this.clearSelections();
15815         }
15816     },
15817
15818     /** @ignore */
15819     onContextMenu : function(e){
15820         var item = this.findItemFromChild(e.getTarget());
15821         if(item){
15822             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15823         }
15824     },
15825
15826     /** @ignore */
15827     onDblClick : function(e){
15828         var item = this.findItemFromChild(e.getTarget());
15829         if(item){
15830             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15831         }
15832     },
15833
15834     onItemClick : function(item, index, e)
15835     {
15836         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15837             return false;
15838         }
15839         if (this.toggleSelect) {
15840             var m = this.isSelected(item) ? 'unselect' : 'select';
15841             //Roo.log(m);
15842             var _t = this;
15843             _t[m](item, true, false);
15844             return true;
15845         }
15846         if(this.multiSelect || this.singleSelect){
15847             if(this.multiSelect && e.shiftKey && this.lastSelection){
15848                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15849             }else{
15850                 this.select(item, this.multiSelect && e.ctrlKey);
15851                 this.lastSelection = item;
15852             }
15853             
15854             if(!this.tickable){
15855                 e.preventDefault();
15856             }
15857             
15858         }
15859         return true;
15860     },
15861
15862     /**
15863      * Get the number of selected nodes.
15864      * @return {Number}
15865      */
15866     getSelectionCount : function(){
15867         return this.selections.length;
15868     },
15869
15870     /**
15871      * Get the currently selected nodes.
15872      * @return {Array} An array of HTMLElements
15873      */
15874     getSelectedNodes : function(){
15875         return this.selections;
15876     },
15877
15878     /**
15879      * Get the indexes of the selected nodes.
15880      * @return {Array}
15881      */
15882     getSelectedIndexes : function(){
15883         var indexes = [], s = this.selections;
15884         for(var i = 0, len = s.length; i < len; i++){
15885             indexes.push(s[i].nodeIndex);
15886         }
15887         return indexes;
15888     },
15889
15890     /**
15891      * Clear all selections
15892      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15893      */
15894     clearSelections : function(suppressEvent){
15895         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15896             this.cmp.elements = this.selections;
15897             this.cmp.removeClass(this.selectedClass);
15898             this.selections = [];
15899             if(!suppressEvent){
15900                 this.fireEvent("selectionchange", this, this.selections);
15901             }
15902         }
15903     },
15904
15905     /**
15906      * Returns true if the passed node is selected
15907      * @param {HTMLElement/Number} node The node or node index
15908      * @return {Boolean}
15909      */
15910     isSelected : function(node){
15911         var s = this.selections;
15912         if(s.length < 1){
15913             return false;
15914         }
15915         node = this.getNode(node);
15916         return s.indexOf(node) !== -1;
15917     },
15918
15919     /**
15920      * Selects nodes.
15921      * @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
15922      * @param {Boolean} keepExisting (optional) true to keep existing selections
15923      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15924      */
15925     select : function(nodeInfo, keepExisting, suppressEvent){
15926         if(nodeInfo instanceof Array){
15927             if(!keepExisting){
15928                 this.clearSelections(true);
15929             }
15930             for(var i = 0, len = nodeInfo.length; i < len; i++){
15931                 this.select(nodeInfo[i], true, true);
15932             }
15933             return;
15934         } 
15935         var node = this.getNode(nodeInfo);
15936         if(!node || this.isSelected(node)){
15937             return; // already selected.
15938         }
15939         if(!keepExisting){
15940             this.clearSelections(true);
15941         }
15942         
15943         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15944             Roo.fly(node).addClass(this.selectedClass);
15945             this.selections.push(node);
15946             if(!suppressEvent){
15947                 this.fireEvent("selectionchange", this, this.selections);
15948             }
15949         }
15950         
15951         
15952     },
15953       /**
15954      * Unselects nodes.
15955      * @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
15956      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15957      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15958      */
15959     unselect : function(nodeInfo, keepExisting, suppressEvent)
15960     {
15961         if(nodeInfo instanceof Array){
15962             Roo.each(this.selections, function(s) {
15963                 this.unselect(s, nodeInfo);
15964             }, this);
15965             return;
15966         }
15967         var node = this.getNode(nodeInfo);
15968         if(!node || !this.isSelected(node)){
15969             //Roo.log("not selected");
15970             return; // not selected.
15971         }
15972         // fireevent???
15973         var ns = [];
15974         Roo.each(this.selections, function(s) {
15975             if (s == node ) {
15976                 Roo.fly(node).removeClass(this.selectedClass);
15977
15978                 return;
15979             }
15980             ns.push(s);
15981         },this);
15982         
15983         this.selections= ns;
15984         this.fireEvent("selectionchange", this, this.selections);
15985     },
15986
15987     /**
15988      * Gets a template node.
15989      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15990      * @return {HTMLElement} The node or null if it wasn't found
15991      */
15992     getNode : function(nodeInfo){
15993         if(typeof nodeInfo == "string"){
15994             return document.getElementById(nodeInfo);
15995         }else if(typeof nodeInfo == "number"){
15996             return this.nodes[nodeInfo];
15997         }
15998         return nodeInfo;
15999     },
16000
16001     /**
16002      * Gets a range template nodes.
16003      * @param {Number} startIndex
16004      * @param {Number} endIndex
16005      * @return {Array} An array of nodes
16006      */
16007     getNodes : function(start, end){
16008         var ns = this.nodes;
16009         start = start || 0;
16010         end = typeof end == "undefined" ? ns.length - 1 : end;
16011         var nodes = [];
16012         if(start <= end){
16013             for(var i = start; i <= end; i++){
16014                 nodes.push(ns[i]);
16015             }
16016         } else{
16017             for(var i = start; i >= end; i--){
16018                 nodes.push(ns[i]);
16019             }
16020         }
16021         return nodes;
16022     },
16023
16024     /**
16025      * Finds the index of the passed node
16026      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16027      * @return {Number} The index of the node or -1
16028      */
16029     indexOf : function(node){
16030         node = this.getNode(node);
16031         if(typeof node.nodeIndex == "number"){
16032             return node.nodeIndex;
16033         }
16034         var ns = this.nodes;
16035         for(var i = 0, len = ns.length; i < len; i++){
16036             if(ns[i] == node){
16037                 return i;
16038             }
16039         }
16040         return -1;
16041     }
16042 });
16043 /*
16044  * - LGPL
16045  *
16046  * based on jquery fullcalendar
16047  * 
16048  */
16049
16050 Roo.bootstrap = Roo.bootstrap || {};
16051 /**
16052  * @class Roo.bootstrap.Calendar
16053  * @extends Roo.bootstrap.Component
16054  * Bootstrap Calendar class
16055  * @cfg {Boolean} loadMask (true|false) default false
16056  * @cfg {Object} header generate the user specific header of the calendar, default false
16057
16058  * @constructor
16059  * Create a new Container
16060  * @param {Object} config The config object
16061  */
16062
16063
16064
16065 Roo.bootstrap.Calendar = function(config){
16066     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16067      this.addEvents({
16068         /**
16069              * @event select
16070              * Fires when a date is selected
16071              * @param {DatePicker} this
16072              * @param {Date} date The selected date
16073              */
16074         'select': true,
16075         /**
16076              * @event monthchange
16077              * Fires when the displayed month changes 
16078              * @param {DatePicker} this
16079              * @param {Date} date The selected month
16080              */
16081         'monthchange': true,
16082         /**
16083              * @event evententer
16084              * Fires when mouse over an event
16085              * @param {Calendar} this
16086              * @param {event} Event
16087              */
16088         'evententer': true,
16089         /**
16090              * @event eventleave
16091              * Fires when the mouse leaves an
16092              * @param {Calendar} this
16093              * @param {event}
16094              */
16095         'eventleave': true,
16096         /**
16097              * @event eventclick
16098              * Fires when the mouse click an
16099              * @param {Calendar} this
16100              * @param {event}
16101              */
16102         'eventclick': true
16103         
16104     });
16105
16106 };
16107
16108 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16109     
16110      /**
16111      * @cfg {Number} startDay
16112      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16113      */
16114     startDay : 0,
16115     
16116     loadMask : false,
16117     
16118     header : false,
16119       
16120     getAutoCreate : function(){
16121         
16122         
16123         var fc_button = function(name, corner, style, content ) {
16124             return Roo.apply({},{
16125                 tag : 'span',
16126                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16127                          (corner.length ?
16128                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16129                             ''
16130                         ),
16131                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16132                 unselectable: 'on'
16133             });
16134         };
16135         
16136         var header = {};
16137         
16138         if(!this.header){
16139             header = {
16140                 tag : 'table',
16141                 cls : 'fc-header',
16142                 style : 'width:100%',
16143                 cn : [
16144                     {
16145                         tag: 'tr',
16146                         cn : [
16147                             {
16148                                 tag : 'td',
16149                                 cls : 'fc-header-left',
16150                                 cn : [
16151                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16152                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16153                                     { tag: 'span', cls: 'fc-header-space' },
16154                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16155
16156
16157                                 ]
16158                             },
16159
16160                             {
16161                                 tag : 'td',
16162                                 cls : 'fc-header-center',
16163                                 cn : [
16164                                     {
16165                                         tag: 'span',
16166                                         cls: 'fc-header-title',
16167                                         cn : {
16168                                             tag: 'H2',
16169                                             html : 'month / year'
16170                                         }
16171                                     }
16172
16173                                 ]
16174                             },
16175                             {
16176                                 tag : 'td',
16177                                 cls : 'fc-header-right',
16178                                 cn : [
16179                               /*      fc_button('month', 'left', '', 'month' ),
16180                                     fc_button('week', '', '', 'week' ),
16181                                     fc_button('day', 'right', '', 'day' )
16182                                 */    
16183
16184                                 ]
16185                             }
16186
16187                         ]
16188                     }
16189                 ]
16190             };
16191         }
16192         
16193         header = this.header;
16194         
16195        
16196         var cal_heads = function() {
16197             var ret = [];
16198             // fixme - handle this.
16199             
16200             for (var i =0; i < Date.dayNames.length; i++) {
16201                 var d = Date.dayNames[i];
16202                 ret.push({
16203                     tag: 'th',
16204                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16205                     html : d.substring(0,3)
16206                 });
16207                 
16208             }
16209             ret[0].cls += ' fc-first';
16210             ret[6].cls += ' fc-last';
16211             return ret;
16212         };
16213         var cal_cell = function(n) {
16214             return  {
16215                 tag: 'td',
16216                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16217                 cn : [
16218                     {
16219                         cn : [
16220                             {
16221                                 cls: 'fc-day-number',
16222                                 html: 'D'
16223                             },
16224                             {
16225                                 cls: 'fc-day-content',
16226                              
16227                                 cn : [
16228                                      {
16229                                         style: 'position: relative;' // height: 17px;
16230                                     }
16231                                 ]
16232                             }
16233                             
16234                             
16235                         ]
16236                     }
16237                 ]
16238                 
16239             }
16240         };
16241         var cal_rows = function() {
16242             
16243             var ret = [];
16244             for (var r = 0; r < 6; r++) {
16245                 var row= {
16246                     tag : 'tr',
16247                     cls : 'fc-week',
16248                     cn : []
16249                 };
16250                 
16251                 for (var i =0; i < Date.dayNames.length; i++) {
16252                     var d = Date.dayNames[i];
16253                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16254
16255                 }
16256                 row.cn[0].cls+=' fc-first';
16257                 row.cn[0].cn[0].style = 'min-height:90px';
16258                 row.cn[6].cls+=' fc-last';
16259                 ret.push(row);
16260                 
16261             }
16262             ret[0].cls += ' fc-first';
16263             ret[4].cls += ' fc-prev-last';
16264             ret[5].cls += ' fc-last';
16265             return ret;
16266             
16267         };
16268         
16269         var cal_table = {
16270             tag: 'table',
16271             cls: 'fc-border-separate',
16272             style : 'width:100%',
16273             cellspacing  : 0,
16274             cn : [
16275                 { 
16276                     tag: 'thead',
16277                     cn : [
16278                         { 
16279                             tag: 'tr',
16280                             cls : 'fc-first fc-last',
16281                             cn : cal_heads()
16282                         }
16283                     ]
16284                 },
16285                 { 
16286                     tag: 'tbody',
16287                     cn : cal_rows()
16288                 }
16289                   
16290             ]
16291         };
16292          
16293          var cfg = {
16294             cls : 'fc fc-ltr',
16295             cn : [
16296                 header,
16297                 {
16298                     cls : 'fc-content',
16299                     style : "position: relative;",
16300                     cn : [
16301                         {
16302                             cls : 'fc-view fc-view-month fc-grid',
16303                             style : 'position: relative',
16304                             unselectable : 'on',
16305                             cn : [
16306                                 {
16307                                     cls : 'fc-event-container',
16308                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16309                                 },
16310                                 cal_table
16311                             ]
16312                         }
16313                     ]
16314     
16315                 }
16316            ] 
16317             
16318         };
16319         
16320          
16321         
16322         return cfg;
16323     },
16324     
16325     
16326     initEvents : function()
16327     {
16328         if(!this.store){
16329             throw "can not find store for calendar";
16330         }
16331         
16332         var mark = {
16333             tag: "div",
16334             cls:"x-dlg-mask",
16335             style: "text-align:center",
16336             cn: [
16337                 {
16338                     tag: "div",
16339                     style: "background-color:white;width:50%;margin:250 auto",
16340                     cn: [
16341                         {
16342                             tag: "img",
16343                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16344                         },
16345                         {
16346                             tag: "span",
16347                             html: "Loading"
16348                         }
16349                         
16350                     ]
16351                 }
16352             ]
16353         };
16354         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16355         
16356         var size = this.el.select('.fc-content', true).first().getSize();
16357         this.maskEl.setSize(size.width, size.height);
16358         this.maskEl.enableDisplayMode("block");
16359         if(!this.loadMask){
16360             this.maskEl.hide();
16361         }
16362         
16363         this.store = Roo.factory(this.store, Roo.data);
16364         this.store.on('load', this.onLoad, this);
16365         this.store.on('beforeload', this.onBeforeLoad, this);
16366         
16367         this.resize();
16368         
16369         this.cells = this.el.select('.fc-day',true);
16370         //Roo.log(this.cells);
16371         this.textNodes = this.el.query('.fc-day-number');
16372         this.cells.addClassOnOver('fc-state-hover');
16373         
16374         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16375         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16376         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16377         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16378         
16379         this.on('monthchange', this.onMonthChange, this);
16380         
16381         this.update(new Date().clearTime());
16382     },
16383     
16384     resize : function() {
16385         var sz  = this.el.getSize();
16386         
16387         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16388         this.el.select('.fc-day-content div',true).setHeight(34);
16389     },
16390     
16391     
16392     // private
16393     showPrevMonth : function(e){
16394         this.update(this.activeDate.add("mo", -1));
16395     },
16396     showToday : function(e){
16397         this.update(new Date().clearTime());
16398     },
16399     // private
16400     showNextMonth : function(e){
16401         this.update(this.activeDate.add("mo", 1));
16402     },
16403
16404     // private
16405     showPrevYear : function(){
16406         this.update(this.activeDate.add("y", -1));
16407     },
16408
16409     // private
16410     showNextYear : function(){
16411         this.update(this.activeDate.add("y", 1));
16412     },
16413
16414     
16415    // private
16416     update : function(date)
16417     {
16418         var vd = this.activeDate;
16419         this.activeDate = date;
16420 //        if(vd && this.el){
16421 //            var t = date.getTime();
16422 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16423 //                Roo.log('using add remove');
16424 //                
16425 //                this.fireEvent('monthchange', this, date);
16426 //                
16427 //                this.cells.removeClass("fc-state-highlight");
16428 //                this.cells.each(function(c){
16429 //                   if(c.dateValue == t){
16430 //                       c.addClass("fc-state-highlight");
16431 //                       setTimeout(function(){
16432 //                            try{c.dom.firstChild.focus();}catch(e){}
16433 //                       }, 50);
16434 //                       return false;
16435 //                   }
16436 //                   return true;
16437 //                });
16438 //                return;
16439 //            }
16440 //        }
16441         
16442         var days = date.getDaysInMonth();
16443         
16444         var firstOfMonth = date.getFirstDateOfMonth();
16445         var startingPos = firstOfMonth.getDay()-this.startDay;
16446         
16447         if(startingPos < this.startDay){
16448             startingPos += 7;
16449         }
16450         
16451         var pm = date.add(Date.MONTH, -1);
16452         var prevStart = pm.getDaysInMonth()-startingPos;
16453 //        
16454         this.cells = this.el.select('.fc-day',true);
16455         this.textNodes = this.el.query('.fc-day-number');
16456         this.cells.addClassOnOver('fc-state-hover');
16457         
16458         var cells = this.cells.elements;
16459         var textEls = this.textNodes;
16460         
16461         Roo.each(cells, function(cell){
16462             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16463         });
16464         
16465         days += startingPos;
16466
16467         // convert everything to numbers so it's fast
16468         var day = 86400000;
16469         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16470         //Roo.log(d);
16471         //Roo.log(pm);
16472         //Roo.log(prevStart);
16473         
16474         var today = new Date().clearTime().getTime();
16475         var sel = date.clearTime().getTime();
16476         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16477         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16478         var ddMatch = this.disabledDatesRE;
16479         var ddText = this.disabledDatesText;
16480         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16481         var ddaysText = this.disabledDaysText;
16482         var format = this.format;
16483         
16484         var setCellClass = function(cal, cell){
16485             cell.row = 0;
16486             cell.events = [];
16487             cell.more = [];
16488             //Roo.log('set Cell Class');
16489             cell.title = "";
16490             var t = d.getTime();
16491             
16492             //Roo.log(d);
16493             
16494             cell.dateValue = t;
16495             if(t == today){
16496                 cell.className += " fc-today";
16497                 cell.className += " fc-state-highlight";
16498                 cell.title = cal.todayText;
16499             }
16500             if(t == sel){
16501                 // disable highlight in other month..
16502                 //cell.className += " fc-state-highlight";
16503                 
16504             }
16505             // disabling
16506             if(t < min) {
16507                 cell.className = " fc-state-disabled";
16508                 cell.title = cal.minText;
16509                 return;
16510             }
16511             if(t > max) {
16512                 cell.className = " fc-state-disabled";
16513                 cell.title = cal.maxText;
16514                 return;
16515             }
16516             if(ddays){
16517                 if(ddays.indexOf(d.getDay()) != -1){
16518                     cell.title = ddaysText;
16519                     cell.className = " fc-state-disabled";
16520                 }
16521             }
16522             if(ddMatch && format){
16523                 var fvalue = d.dateFormat(format);
16524                 if(ddMatch.test(fvalue)){
16525                     cell.title = ddText.replace("%0", fvalue);
16526                     cell.className = " fc-state-disabled";
16527                 }
16528             }
16529             
16530             if (!cell.initialClassName) {
16531                 cell.initialClassName = cell.dom.className;
16532             }
16533             
16534             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16535         };
16536
16537         var i = 0;
16538         
16539         for(; i < startingPos; i++) {
16540             textEls[i].innerHTML = (++prevStart);
16541             d.setDate(d.getDate()+1);
16542             
16543             cells[i].className = "fc-past fc-other-month";
16544             setCellClass(this, cells[i]);
16545         }
16546         
16547         var intDay = 0;
16548         
16549         for(; i < days; i++){
16550             intDay = i - startingPos + 1;
16551             textEls[i].innerHTML = (intDay);
16552             d.setDate(d.getDate()+1);
16553             
16554             cells[i].className = ''; // "x-date-active";
16555             setCellClass(this, cells[i]);
16556         }
16557         var extraDays = 0;
16558         
16559         for(; i < 42; i++) {
16560             textEls[i].innerHTML = (++extraDays);
16561             d.setDate(d.getDate()+1);
16562             
16563             cells[i].className = "fc-future fc-other-month";
16564             setCellClass(this, cells[i]);
16565         }
16566         
16567         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16568         
16569         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16570         
16571         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16572         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16573         
16574         if(totalRows != 6){
16575             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16576             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16577         }
16578         
16579         this.fireEvent('monthchange', this, date);
16580         
16581         
16582         /*
16583         if(!this.internalRender){
16584             var main = this.el.dom.firstChild;
16585             var w = main.offsetWidth;
16586             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16587             Roo.fly(main).setWidth(w);
16588             this.internalRender = true;
16589             // opera does not respect the auto grow header center column
16590             // then, after it gets a width opera refuses to recalculate
16591             // without a second pass
16592             if(Roo.isOpera && !this.secondPass){
16593                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16594                 this.secondPass = true;
16595                 this.update.defer(10, this, [date]);
16596             }
16597         }
16598         */
16599         
16600     },
16601     
16602     findCell : function(dt) {
16603         dt = dt.clearTime().getTime();
16604         var ret = false;
16605         this.cells.each(function(c){
16606             //Roo.log("check " +c.dateValue + '?=' + dt);
16607             if(c.dateValue == dt){
16608                 ret = c;
16609                 return false;
16610             }
16611             return true;
16612         });
16613         
16614         return ret;
16615     },
16616     
16617     findCells : function(ev) {
16618         var s = ev.start.clone().clearTime().getTime();
16619        // Roo.log(s);
16620         var e= ev.end.clone().clearTime().getTime();
16621        // Roo.log(e);
16622         var ret = [];
16623         this.cells.each(function(c){
16624              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16625             
16626             if(c.dateValue > e){
16627                 return ;
16628             }
16629             if(c.dateValue < s){
16630                 return ;
16631             }
16632             ret.push(c);
16633         });
16634         
16635         return ret;    
16636     },
16637     
16638 //    findBestRow: function(cells)
16639 //    {
16640 //        var ret = 0;
16641 //        
16642 //        for (var i =0 ; i < cells.length;i++) {
16643 //            ret  = Math.max(cells[i].rows || 0,ret);
16644 //        }
16645 //        return ret;
16646 //        
16647 //    },
16648     
16649     
16650     addItem : function(ev)
16651     {
16652         // look for vertical location slot in
16653         var cells = this.findCells(ev);
16654         
16655 //        ev.row = this.findBestRow(cells);
16656         
16657         // work out the location.
16658         
16659         var crow = false;
16660         var rows = [];
16661         for(var i =0; i < cells.length; i++) {
16662             
16663             cells[i].row = cells[0].row;
16664             
16665             if(i == 0){
16666                 cells[i].row = cells[i].row + 1;
16667             }
16668             
16669             if (!crow) {
16670                 crow = {
16671                     start : cells[i],
16672                     end :  cells[i]
16673                 };
16674                 continue;
16675             }
16676             if (crow.start.getY() == cells[i].getY()) {
16677                 // on same row.
16678                 crow.end = cells[i];
16679                 continue;
16680             }
16681             // different row.
16682             rows.push(crow);
16683             crow = {
16684                 start: cells[i],
16685                 end : cells[i]
16686             };
16687             
16688         }
16689         
16690         rows.push(crow);
16691         ev.els = [];
16692         ev.rows = rows;
16693         ev.cells = cells;
16694         
16695         cells[0].events.push(ev);
16696         
16697         this.calevents.push(ev);
16698     },
16699     
16700     clearEvents: function() {
16701         
16702         if(!this.calevents){
16703             return;
16704         }
16705         
16706         Roo.each(this.cells.elements, function(c){
16707             c.row = 0;
16708             c.events = [];
16709             c.more = [];
16710         });
16711         
16712         Roo.each(this.calevents, function(e) {
16713             Roo.each(e.els, function(el) {
16714                 el.un('mouseenter' ,this.onEventEnter, this);
16715                 el.un('mouseleave' ,this.onEventLeave, this);
16716                 el.remove();
16717             },this);
16718         },this);
16719         
16720         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16721             e.remove();
16722         });
16723         
16724     },
16725     
16726     renderEvents: function()
16727     {   
16728         var _this = this;
16729         
16730         this.cells.each(function(c) {
16731             
16732             if(c.row < 5){
16733                 return;
16734             }
16735             
16736             var ev = c.events;
16737             
16738             var r = 4;
16739             if(c.row != c.events.length){
16740                 r = 4 - (4 - (c.row - c.events.length));
16741             }
16742             
16743             c.events = ev.slice(0, r);
16744             c.more = ev.slice(r);
16745             
16746             if(c.more.length && c.more.length == 1){
16747                 c.events.push(c.more.pop());
16748             }
16749             
16750             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16751             
16752         });
16753             
16754         this.cells.each(function(c) {
16755             
16756             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16757             
16758             
16759             for (var e = 0; e < c.events.length; e++){
16760                 var ev = c.events[e];
16761                 var rows = ev.rows;
16762                 
16763                 for(var i = 0; i < rows.length; i++) {
16764                 
16765                     // how many rows should it span..
16766
16767                     var  cfg = {
16768                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16769                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16770
16771                         unselectable : "on",
16772                         cn : [
16773                             {
16774                                 cls: 'fc-event-inner',
16775                                 cn : [
16776     //                                {
16777     //                                  tag:'span',
16778     //                                  cls: 'fc-event-time',
16779     //                                  html : cells.length > 1 ? '' : ev.time
16780     //                                },
16781                                     {
16782                                       tag:'span',
16783                                       cls: 'fc-event-title',
16784                                       html : String.format('{0}', ev.title)
16785                                     }
16786
16787
16788                                 ]
16789                             },
16790                             {
16791                                 cls: 'ui-resizable-handle ui-resizable-e',
16792                                 html : '&nbsp;&nbsp;&nbsp'
16793                             }
16794
16795                         ]
16796                     };
16797
16798                     if (i == 0) {
16799                         cfg.cls += ' fc-event-start';
16800                     }
16801                     if ((i+1) == rows.length) {
16802                         cfg.cls += ' fc-event-end';
16803                     }
16804
16805                     var ctr = _this.el.select('.fc-event-container',true).first();
16806                     var cg = ctr.createChild(cfg);
16807
16808                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16809                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16810
16811                     var r = (c.more.length) ? 1 : 0;
16812                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16813                     cg.setWidth(ebox.right - sbox.x -2);
16814
16815                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16816                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16817                     cg.on('click', _this.onEventClick, _this, ev);
16818
16819                     ev.els.push(cg);
16820                     
16821                 }
16822                 
16823             }
16824             
16825             
16826             if(c.more.length){
16827                 var  cfg = {
16828                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16829                     style : 'position: absolute',
16830                     unselectable : "on",
16831                     cn : [
16832                         {
16833                             cls: 'fc-event-inner',
16834                             cn : [
16835                                 {
16836                                   tag:'span',
16837                                   cls: 'fc-event-title',
16838                                   html : 'More'
16839                                 }
16840
16841
16842                             ]
16843                         },
16844                         {
16845                             cls: 'ui-resizable-handle ui-resizable-e',
16846                             html : '&nbsp;&nbsp;&nbsp'
16847                         }
16848
16849                     ]
16850                 };
16851
16852                 var ctr = _this.el.select('.fc-event-container',true).first();
16853                 var cg = ctr.createChild(cfg);
16854
16855                 var sbox = c.select('.fc-day-content',true).first().getBox();
16856                 var ebox = c.select('.fc-day-content',true).first().getBox();
16857                 //Roo.log(cg);
16858                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16859                 cg.setWidth(ebox.right - sbox.x -2);
16860
16861                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16862                 
16863             }
16864             
16865         });
16866         
16867         
16868         
16869     },
16870     
16871     onEventEnter: function (e, el,event,d) {
16872         this.fireEvent('evententer', this, el, event);
16873     },
16874     
16875     onEventLeave: function (e, el,event,d) {
16876         this.fireEvent('eventleave', this, el, event);
16877     },
16878     
16879     onEventClick: function (e, el,event,d) {
16880         this.fireEvent('eventclick', this, el, event);
16881     },
16882     
16883     onMonthChange: function () {
16884         this.store.load();
16885     },
16886     
16887     onMoreEventClick: function(e, el, more)
16888     {
16889         var _this = this;
16890         
16891         this.calpopover.placement = 'right';
16892         this.calpopover.setTitle('More');
16893         
16894         this.calpopover.setContent('');
16895         
16896         var ctr = this.calpopover.el.select('.popover-content', true).first();
16897         
16898         Roo.each(more, function(m){
16899             var cfg = {
16900                 cls : 'fc-event-hori fc-event-draggable',
16901                 html : m.title
16902             };
16903             var cg = ctr.createChild(cfg);
16904             
16905             cg.on('click', _this.onEventClick, _this, m);
16906         });
16907         
16908         this.calpopover.show(el);
16909         
16910         
16911     },
16912     
16913     onLoad: function () 
16914     {   
16915         this.calevents = [];
16916         var cal = this;
16917         
16918         if(this.store.getCount() > 0){
16919             this.store.data.each(function(d){
16920                cal.addItem({
16921                     id : d.data.id,
16922                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16923                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16924                     time : d.data.start_time,
16925                     title : d.data.title,
16926                     description : d.data.description,
16927                     venue : d.data.venue
16928                 });
16929             });
16930         }
16931         
16932         this.renderEvents();
16933         
16934         if(this.calevents.length && this.loadMask){
16935             this.maskEl.hide();
16936         }
16937     },
16938     
16939     onBeforeLoad: function()
16940     {
16941         this.clearEvents();
16942         if(this.loadMask){
16943             this.maskEl.show();
16944         }
16945     }
16946 });
16947
16948  
16949  /*
16950  * - LGPL
16951  *
16952  * element
16953  * 
16954  */
16955
16956 /**
16957  * @class Roo.bootstrap.Popover
16958  * @extends Roo.bootstrap.Component
16959  * Bootstrap Popover class
16960  * @cfg {String} html contents of the popover   (or false to use children..)
16961  * @cfg {String} title of popover (or false to hide)
16962  * @cfg {String} placement how it is placed
16963  * @cfg {String} trigger click || hover (or false to trigger manually)
16964  * @cfg {String} over what (parent or false to trigger manually.)
16965  * @cfg {Number} delay - delay before showing
16966  
16967  * @constructor
16968  * Create a new Popover
16969  * @param {Object} config The config object
16970  */
16971
16972 Roo.bootstrap.Popover = function(config){
16973     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16974     
16975     this.addEvents({
16976         // raw events
16977          /**
16978          * @event show
16979          * After the popover show
16980          * 
16981          * @param {Roo.bootstrap.Popover} this
16982          */
16983         "show" : true,
16984         /**
16985          * @event hide
16986          * After the popover hide
16987          * 
16988          * @param {Roo.bootstrap.Popover} this
16989          */
16990         "hide" : true
16991     });
16992 };
16993
16994 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16995     
16996     title: 'Fill in a title',
16997     html: false,
16998     
16999     placement : 'right',
17000     trigger : 'hover', // hover
17001     
17002     delay : 0,
17003     
17004     over: 'parent',
17005     
17006     can_build_overlaid : false,
17007     
17008     getChildContainer : function()
17009     {
17010         return this.el.select('.popover-content',true).first();
17011     },
17012     
17013     getAutoCreate : function(){
17014          
17015         var cfg = {
17016            cls : 'popover roo-dynamic',
17017            style: 'display:block',
17018            cn : [
17019                 {
17020                     cls : 'arrow'
17021                 },
17022                 {
17023                     cls : 'popover-inner',
17024                     cn : [
17025                         {
17026                             tag: 'h3',
17027                             cls: 'popover-title',
17028                             html : this.title
17029                         },
17030                         {
17031                             cls : 'popover-content',
17032                             html : this.html
17033                         }
17034                     ]
17035                     
17036                 }
17037            ]
17038         };
17039         
17040         return cfg;
17041     },
17042     setTitle: function(str)
17043     {
17044         this.title = str;
17045         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17046     },
17047     setContent: function(str)
17048     {
17049         this.html = str;
17050         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17051     },
17052     // as it get's added to the bottom of the page.
17053     onRender : function(ct, position)
17054     {
17055         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17056         if(!this.el){
17057             var cfg = Roo.apply({},  this.getAutoCreate());
17058             cfg.id = Roo.id();
17059             
17060             if (this.cls) {
17061                 cfg.cls += ' ' + this.cls;
17062             }
17063             if (this.style) {
17064                 cfg.style = this.style;
17065             }
17066             //Roo.log("adding to ");
17067             this.el = Roo.get(document.body).createChild(cfg, position);
17068 //            Roo.log(this.el);
17069         }
17070         this.initEvents();
17071     },
17072     
17073     initEvents : function()
17074     {
17075         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17076         this.el.enableDisplayMode('block');
17077         this.el.hide();
17078         if (this.over === false) {
17079             return; 
17080         }
17081         if (this.triggers === false) {
17082             return;
17083         }
17084         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17085         var triggers = this.trigger ? this.trigger.split(' ') : [];
17086         Roo.each(triggers, function(trigger) {
17087         
17088             if (trigger == 'click') {
17089                 on_el.on('click', this.toggle, this);
17090             } else if (trigger != 'manual') {
17091                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17092                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17093       
17094                 on_el.on(eventIn  ,this.enter, this);
17095                 on_el.on(eventOut, this.leave, this);
17096             }
17097         }, this);
17098         
17099     },
17100     
17101     
17102     // private
17103     timeout : null,
17104     hoverState : null,
17105     
17106     toggle : function () {
17107         this.hoverState == 'in' ? this.leave() : this.enter();
17108     },
17109     
17110     enter : function () {
17111         
17112         clearTimeout(this.timeout);
17113     
17114         this.hoverState = 'in';
17115     
17116         if (!this.delay || !this.delay.show) {
17117             this.show();
17118             return;
17119         }
17120         var _t = this;
17121         this.timeout = setTimeout(function () {
17122             if (_t.hoverState == 'in') {
17123                 _t.show();
17124             }
17125         }, this.delay.show)
17126     },
17127     
17128     leave : function() {
17129         clearTimeout(this.timeout);
17130     
17131         this.hoverState = 'out';
17132     
17133         if (!this.delay || !this.delay.hide) {
17134             this.hide();
17135             return;
17136         }
17137         var _t = this;
17138         this.timeout = setTimeout(function () {
17139             if (_t.hoverState == 'out') {
17140                 _t.hide();
17141             }
17142         }, this.delay.hide)
17143     },
17144     
17145     show : function (on_el)
17146     {
17147         if (!on_el) {
17148             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17149         }
17150         
17151         // set content.
17152         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17153         if (this.html !== false) {
17154             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17155         }
17156         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17157         if (!this.title.length) {
17158             this.el.select('.popover-title',true).hide();
17159         }
17160         
17161         var placement = typeof this.placement == 'function' ?
17162             this.placement.call(this, this.el, on_el) :
17163             this.placement;
17164             
17165         var autoToken = /\s?auto?\s?/i;
17166         var autoPlace = autoToken.test(placement);
17167         if (autoPlace) {
17168             placement = placement.replace(autoToken, '') || 'top';
17169         }
17170         
17171         //this.el.detach()
17172         //this.el.setXY([0,0]);
17173         this.el.show();
17174         this.el.dom.style.display='block';
17175         this.el.addClass(placement);
17176         
17177         //this.el.appendTo(on_el);
17178         
17179         var p = this.getPosition();
17180         var box = this.el.getBox();
17181         
17182         if (autoPlace) {
17183             // fixme..
17184         }
17185         var align = Roo.bootstrap.Popover.alignment[placement];
17186         this.el.alignTo(on_el, align[0],align[1]);
17187         //var arrow = this.el.select('.arrow',true).first();
17188         //arrow.set(align[2], 
17189         
17190         this.el.addClass('in');
17191         
17192         
17193         if (this.el.hasClass('fade')) {
17194             // fade it?
17195         }
17196         
17197         this.hoverState = 'in';
17198         
17199         this.fireEvent('show', this);
17200         
17201     },
17202     hide : function()
17203     {
17204         this.el.setXY([0,0]);
17205         this.el.removeClass('in');
17206         this.el.hide();
17207         this.hoverState = null;
17208         
17209         this.fireEvent('hide', this);
17210     }
17211     
17212 });
17213
17214 Roo.bootstrap.Popover.alignment = {
17215     'left' : ['r-l', [-10,0], 'right'],
17216     'right' : ['l-r', [10,0], 'left'],
17217     'bottom' : ['t-b', [0,10], 'top'],
17218     'top' : [ 'b-t', [0,-10], 'bottom']
17219 };
17220
17221  /*
17222  * - LGPL
17223  *
17224  * Progress
17225  * 
17226  */
17227
17228 /**
17229  * @class Roo.bootstrap.Progress
17230  * @extends Roo.bootstrap.Component
17231  * Bootstrap Progress class
17232  * @cfg {Boolean} striped striped of the progress bar
17233  * @cfg {Boolean} active animated of the progress bar
17234  * 
17235  * 
17236  * @constructor
17237  * Create a new Progress
17238  * @param {Object} config The config object
17239  */
17240
17241 Roo.bootstrap.Progress = function(config){
17242     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17243 };
17244
17245 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17246     
17247     striped : false,
17248     active: false,
17249     
17250     getAutoCreate : function(){
17251         var cfg = {
17252             tag: 'div',
17253             cls: 'progress'
17254         };
17255         
17256         
17257         if(this.striped){
17258             cfg.cls += ' progress-striped';
17259         }
17260       
17261         if(this.active){
17262             cfg.cls += ' active';
17263         }
17264         
17265         
17266         return cfg;
17267     }
17268    
17269 });
17270
17271  
17272
17273  /*
17274  * - LGPL
17275  *
17276  * ProgressBar
17277  * 
17278  */
17279
17280 /**
17281  * @class Roo.bootstrap.ProgressBar
17282  * @extends Roo.bootstrap.Component
17283  * Bootstrap ProgressBar class
17284  * @cfg {Number} aria_valuenow aria-value now
17285  * @cfg {Number} aria_valuemin aria-value min
17286  * @cfg {Number} aria_valuemax aria-value max
17287  * @cfg {String} label label for the progress bar
17288  * @cfg {String} panel (success | info | warning | danger )
17289  * @cfg {String} role role of the progress bar
17290  * @cfg {String} sr_only text
17291  * 
17292  * 
17293  * @constructor
17294  * Create a new ProgressBar
17295  * @param {Object} config The config object
17296  */
17297
17298 Roo.bootstrap.ProgressBar = function(config){
17299     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17300 };
17301
17302 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17303     
17304     aria_valuenow : 0,
17305     aria_valuemin : 0,
17306     aria_valuemax : 100,
17307     label : false,
17308     panel : false,
17309     role : false,
17310     sr_only: false,
17311     
17312     getAutoCreate : function()
17313     {
17314         
17315         var cfg = {
17316             tag: 'div',
17317             cls: 'progress-bar',
17318             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17319         };
17320         
17321         if(this.sr_only){
17322             cfg.cn = {
17323                 tag: 'span',
17324                 cls: 'sr-only',
17325                 html: this.sr_only
17326             }
17327         }
17328         
17329         if(this.role){
17330             cfg.role = this.role;
17331         }
17332         
17333         if(this.aria_valuenow){
17334             cfg['aria-valuenow'] = this.aria_valuenow;
17335         }
17336         
17337         if(this.aria_valuemin){
17338             cfg['aria-valuemin'] = this.aria_valuemin;
17339         }
17340         
17341         if(this.aria_valuemax){
17342             cfg['aria-valuemax'] = this.aria_valuemax;
17343         }
17344         
17345         if(this.label && !this.sr_only){
17346             cfg.html = this.label;
17347         }
17348         
17349         if(this.panel){
17350             cfg.cls += ' progress-bar-' + this.panel;
17351         }
17352         
17353         return cfg;
17354     },
17355     
17356     update : function(aria_valuenow)
17357     {
17358         this.aria_valuenow = aria_valuenow;
17359         
17360         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17361     }
17362    
17363 });
17364
17365  
17366
17367  /*
17368  * - LGPL
17369  *
17370  * column
17371  * 
17372  */
17373
17374 /**
17375  * @class Roo.bootstrap.TabGroup
17376  * @extends Roo.bootstrap.Column
17377  * Bootstrap Column class
17378  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17379  * @cfg {Boolean} carousel true to make the group behave like a carousel
17380  * @cfg {Boolean} bullets show bullets for the panels
17381  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17382  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17383  * @cfg {Boolean} showarrow (true|false) show arrow default true
17384  * 
17385  * @constructor
17386  * Create a new TabGroup
17387  * @param {Object} config The config object
17388  */
17389
17390 Roo.bootstrap.TabGroup = function(config){
17391     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17392     if (!this.navId) {
17393         this.navId = Roo.id();
17394     }
17395     this.tabs = [];
17396     Roo.bootstrap.TabGroup.register(this);
17397     
17398 };
17399
17400 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17401     
17402     carousel : false,
17403     transition : false,
17404     bullets : 0,
17405     timer : 0,
17406     autoslide : false,
17407     slideFn : false,
17408     slideOnTouch : false,
17409     showarrow : true,
17410     
17411     getAutoCreate : function()
17412     {
17413         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17414         
17415         cfg.cls += ' tab-content';
17416         
17417         if (this.carousel) {
17418             cfg.cls += ' carousel slide';
17419             
17420             cfg.cn = [{
17421                cls : 'carousel-inner',
17422                cn : []
17423             }];
17424         
17425             if(this.bullets  && !Roo.isTouch){
17426                 
17427                 var bullets = {
17428                     cls : 'carousel-bullets',
17429                     cn : []
17430                 };
17431                
17432                 if(this.bullets_cls){
17433                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17434                 }
17435                 
17436                 bullets.cn.push({
17437                     cls : 'clear'
17438                 });
17439                 
17440                 cfg.cn[0].cn.push(bullets);
17441             }
17442             
17443             if(this.showarrow){
17444                 cfg.cn[0].cn.push({
17445                     tag : 'div',
17446                     class : 'carousel-arrow',
17447                     cn : [
17448                         {
17449                             tag : 'div',
17450                             class : 'carousel-prev',
17451                             cn : [
17452                                 {
17453                                     tag : 'i',
17454                                     class : 'fa fa-chevron-left'
17455                                 }
17456                             ]
17457                         },
17458                         {
17459                             tag : 'div',
17460                             class : 'carousel-next',
17461                             cn : [
17462                                 {
17463                                     tag : 'i',
17464                                     class : 'fa fa-chevron-right'
17465                                 }
17466                             ]
17467                         }
17468                     ]
17469                 });
17470             }
17471             
17472         }
17473         
17474         return cfg;
17475     },
17476     
17477     initEvents:  function()
17478     {
17479 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17480 //            this.el.on("touchstart", this.onTouchStart, this);
17481 //        }
17482         
17483         if(this.autoslide){
17484             var _this = this;
17485             
17486             this.slideFn = window.setInterval(function() {
17487                 _this.showPanelNext();
17488             }, this.timer);
17489         }
17490         
17491         if(this.showarrow){
17492             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17493             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17494         }
17495         
17496         
17497     },
17498     
17499 //    onTouchStart : function(e, el, o)
17500 //    {
17501 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17502 //            return;
17503 //        }
17504 //        
17505 //        this.showPanelNext();
17506 //    },
17507     
17508     
17509     getChildContainer : function()
17510     {
17511         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17512     },
17513     
17514     /**
17515     * register a Navigation item
17516     * @param {Roo.bootstrap.NavItem} the navitem to add
17517     */
17518     register : function(item)
17519     {
17520         this.tabs.push( item);
17521         item.navId = this.navId; // not really needed..
17522         this.addBullet();
17523     
17524     },
17525     
17526     getActivePanel : function()
17527     {
17528         var r = false;
17529         Roo.each(this.tabs, function(t) {
17530             if (t.active) {
17531                 r = t;
17532                 return false;
17533             }
17534             return null;
17535         });
17536         return r;
17537         
17538     },
17539     getPanelByName : function(n)
17540     {
17541         var r = false;
17542         Roo.each(this.tabs, function(t) {
17543             if (t.tabId == n) {
17544                 r = t;
17545                 return false;
17546             }
17547             return null;
17548         });
17549         return r;
17550     },
17551     indexOfPanel : function(p)
17552     {
17553         var r = false;
17554         Roo.each(this.tabs, function(t,i) {
17555             if (t.tabId == p.tabId) {
17556                 r = i;
17557                 return false;
17558             }
17559             return null;
17560         });
17561         return r;
17562     },
17563     /**
17564      * show a specific panel
17565      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17566      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17567      */
17568     showPanel : function (pan)
17569     {
17570         if(this.transition || typeof(pan) == 'undefined'){
17571             Roo.log("waiting for the transitionend");
17572             return;
17573         }
17574         
17575         if (typeof(pan) == 'number') {
17576             pan = this.tabs[pan];
17577         }
17578         
17579         if (typeof(pan) == 'string') {
17580             pan = this.getPanelByName(pan);
17581         }
17582         
17583         var cur = this.getActivePanel();
17584         
17585         if(!pan || !cur){
17586             Roo.log('pan or acitve pan is undefined');
17587             return false;
17588         }
17589         
17590         if (pan.tabId == this.getActivePanel().tabId) {
17591             return true;
17592         }
17593         
17594         if (false === cur.fireEvent('beforedeactivate')) {
17595             return false;
17596         }
17597         
17598         if(this.bullets > 0 && !Roo.isTouch){
17599             this.setActiveBullet(this.indexOfPanel(pan));
17600         }
17601         
17602         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17603             
17604             this.transition = true;
17605             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17606             var lr = dir == 'next' ? 'left' : 'right';
17607             pan.el.addClass(dir); // or prev
17608             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17609             cur.el.addClass(lr); // or right
17610             pan.el.addClass(lr);
17611             
17612             var _this = this;
17613             cur.el.on('transitionend', function() {
17614                 Roo.log("trans end?");
17615                 
17616                 pan.el.removeClass([lr,dir]);
17617                 pan.setActive(true);
17618                 
17619                 cur.el.removeClass([lr]);
17620                 cur.setActive(false);
17621                 
17622                 _this.transition = false;
17623                 
17624             }, this, { single:  true } );
17625             
17626             return true;
17627         }
17628         
17629         cur.setActive(false);
17630         pan.setActive(true);
17631         
17632         return true;
17633         
17634     },
17635     showPanelNext : function()
17636     {
17637         var i = this.indexOfPanel(this.getActivePanel());
17638         
17639         if (i >= this.tabs.length - 1 && !this.autoslide) {
17640             return;
17641         }
17642         
17643         if (i >= this.tabs.length - 1 && this.autoslide) {
17644             i = -1;
17645         }
17646         
17647         this.showPanel(this.tabs[i+1]);
17648     },
17649     
17650     showPanelPrev : function()
17651     {
17652         var i = this.indexOfPanel(this.getActivePanel());
17653         
17654         if (i  < 1 && !this.autoslide) {
17655             return;
17656         }
17657         
17658         if (i < 1 && this.autoslide) {
17659             i = this.tabs.length;
17660         }
17661         
17662         this.showPanel(this.tabs[i-1]);
17663     },
17664     
17665     
17666     addBullet: function()
17667     {
17668         if(!this.bullets || Roo.isTouch){
17669             return;
17670         }
17671         var ctr = this.el.select('.carousel-bullets',true).first();
17672         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17673         var bullet = ctr.createChild({
17674             cls : 'bullet bullet-' + i
17675         },ctr.dom.lastChild);
17676         
17677         
17678         var _this = this;
17679         
17680         bullet.on('click', (function(e, el, o, ii, t){
17681
17682             e.preventDefault();
17683
17684             this.showPanel(ii);
17685
17686             if(this.autoslide && this.slideFn){
17687                 clearInterval(this.slideFn);
17688                 this.slideFn = window.setInterval(function() {
17689                     _this.showPanelNext();
17690                 }, this.timer);
17691             }
17692
17693         }).createDelegate(this, [i, bullet], true));
17694                 
17695         
17696     },
17697      
17698     setActiveBullet : function(i)
17699     {
17700         if(Roo.isTouch){
17701             return;
17702         }
17703         
17704         Roo.each(this.el.select('.bullet', true).elements, function(el){
17705             el.removeClass('selected');
17706         });
17707
17708         var bullet = this.el.select('.bullet-' + i, true).first();
17709         
17710         if(!bullet){
17711             return;
17712         }
17713         
17714         bullet.addClass('selected');
17715     }
17716     
17717     
17718   
17719 });
17720
17721  
17722
17723  
17724  
17725 Roo.apply(Roo.bootstrap.TabGroup, {
17726     
17727     groups: {},
17728      /**
17729     * register a Navigation Group
17730     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17731     */
17732     register : function(navgrp)
17733     {
17734         this.groups[navgrp.navId] = navgrp;
17735         
17736     },
17737     /**
17738     * fetch a Navigation Group based on the navigation ID
17739     * if one does not exist , it will get created.
17740     * @param {string} the navgroup to add
17741     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17742     */
17743     get: function(navId) {
17744         if (typeof(this.groups[navId]) == 'undefined') {
17745             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17746         }
17747         return this.groups[navId] ;
17748     }
17749     
17750     
17751     
17752 });
17753
17754  /*
17755  * - LGPL
17756  *
17757  * TabPanel
17758  * 
17759  */
17760
17761 /**
17762  * @class Roo.bootstrap.TabPanel
17763  * @extends Roo.bootstrap.Component
17764  * Bootstrap TabPanel class
17765  * @cfg {Boolean} active panel active
17766  * @cfg {String} html panel content
17767  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17768  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17769  * @cfg {String} href click to link..
17770  * 
17771  * 
17772  * @constructor
17773  * Create a new TabPanel
17774  * @param {Object} config The config object
17775  */
17776
17777 Roo.bootstrap.TabPanel = function(config){
17778     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17779     this.addEvents({
17780         /**
17781              * @event changed
17782              * Fires when the active status changes
17783              * @param {Roo.bootstrap.TabPanel} this
17784              * @param {Boolean} state the new state
17785             
17786          */
17787         'changed': true,
17788         /**
17789              * @event beforedeactivate
17790              * Fires before a tab is de-activated - can be used to do validation on a form.
17791              * @param {Roo.bootstrap.TabPanel} this
17792              * @return {Boolean} false if there is an error
17793             
17794          */
17795         'beforedeactivate': true
17796      });
17797     
17798     this.tabId = this.tabId || Roo.id();
17799   
17800 };
17801
17802 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17803     
17804     active: false,
17805     html: false,
17806     tabId: false,
17807     navId : false,
17808     href : '',
17809     
17810     getAutoCreate : function(){
17811         var cfg = {
17812             tag: 'div',
17813             // item is needed for carousel - not sure if it has any effect otherwise
17814             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17815             html: this.html || ''
17816         };
17817         
17818         if(this.active){
17819             cfg.cls += ' active';
17820         }
17821         
17822         if(this.tabId){
17823             cfg.tabId = this.tabId;
17824         }
17825         
17826         
17827         return cfg;
17828     },
17829     
17830     initEvents:  function()
17831     {
17832         var p = this.parent();
17833         
17834         this.navId = this.navId || p.navId;
17835         
17836         if (typeof(this.navId) != 'undefined') {
17837             // not really needed.. but just in case.. parent should be a NavGroup.
17838             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17839             
17840             tg.register(this);
17841             
17842             var i = tg.tabs.length - 1;
17843             
17844             if(this.active && tg.bullets > 0 && i < tg.bullets){
17845                 tg.setActiveBullet(i);
17846             }
17847         }
17848         
17849         this.el.on('click', this.onClick, this);
17850         
17851         if(Roo.isTouch){
17852             this.el.on("touchstart", this.onTouchStart, this);
17853             this.el.on("touchmove", this.onTouchMove, this);
17854             this.el.on("touchend", this.onTouchEnd, this);
17855         }
17856         
17857     },
17858     
17859     onRender : function(ct, position)
17860     {
17861         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17862     },
17863     
17864     setActive : function(state)
17865     {
17866         Roo.log("panel - set active " + this.tabId + "=" + state);
17867         
17868         this.active = state;
17869         if (!state) {
17870             this.el.removeClass('active');
17871             
17872         } else  if (!this.el.hasClass('active')) {
17873             this.el.addClass('active');
17874         }
17875         
17876         this.fireEvent('changed', this, state);
17877     },
17878     
17879     onClick : function(e)
17880     {
17881         e.preventDefault();
17882         
17883         if(!this.href.length){
17884             return;
17885         }
17886         
17887         window.location.href = this.href;
17888     },
17889     
17890     startX : 0,
17891     startY : 0,
17892     endX : 0,
17893     endY : 0,
17894     swiping : false,
17895     
17896     onTouchStart : function(e)
17897     {
17898         this.swiping = false;
17899         
17900         this.startX = e.browserEvent.touches[0].clientX;
17901         this.startY = e.browserEvent.touches[0].clientY;
17902     },
17903     
17904     onTouchMove : function(e)
17905     {
17906         this.swiping = true;
17907         
17908         this.endX = e.browserEvent.touches[0].clientX;
17909         this.endY = e.browserEvent.touches[0].clientY;
17910     },
17911     
17912     onTouchEnd : function(e)
17913     {
17914         if(!this.swiping){
17915             this.onClick(e);
17916             return;
17917         }
17918         
17919         var tabGroup = this.parent();
17920         
17921         if(this.endX > this.startX){ // swiping right
17922             tabGroup.showPanelPrev();
17923             return;
17924         }
17925         
17926         if(this.startX > this.endX){ // swiping left
17927             tabGroup.showPanelNext();
17928             return;
17929         }
17930     }
17931     
17932     
17933 });
17934  
17935
17936  
17937
17938  /*
17939  * - LGPL
17940  *
17941  * DateField
17942  * 
17943  */
17944
17945 /**
17946  * @class Roo.bootstrap.DateField
17947  * @extends Roo.bootstrap.Input
17948  * Bootstrap DateField class
17949  * @cfg {Number} weekStart default 0
17950  * @cfg {String} viewMode default empty, (months|years)
17951  * @cfg {String} minViewMode default empty, (months|years)
17952  * @cfg {Number} startDate default -Infinity
17953  * @cfg {Number} endDate default Infinity
17954  * @cfg {Boolean} todayHighlight default false
17955  * @cfg {Boolean} todayBtn default false
17956  * @cfg {Boolean} calendarWeeks default false
17957  * @cfg {Object} daysOfWeekDisabled default empty
17958  * @cfg {Boolean} singleMode default false (true | false)
17959  * 
17960  * @cfg {Boolean} keyboardNavigation default true
17961  * @cfg {String} language default en
17962  * 
17963  * @constructor
17964  * Create a new DateField
17965  * @param {Object} config The config object
17966  */
17967
17968 Roo.bootstrap.DateField = function(config){
17969     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17970      this.addEvents({
17971             /**
17972              * @event show
17973              * Fires when this field show.
17974              * @param {Roo.bootstrap.DateField} this
17975              * @param {Mixed} date The date value
17976              */
17977             show : true,
17978             /**
17979              * @event show
17980              * Fires when this field hide.
17981              * @param {Roo.bootstrap.DateField} this
17982              * @param {Mixed} date The date value
17983              */
17984             hide : true,
17985             /**
17986              * @event select
17987              * Fires when select a date.
17988              * @param {Roo.bootstrap.DateField} this
17989              * @param {Mixed} date The date value
17990              */
17991             select : true,
17992             /**
17993              * @event beforeselect
17994              * Fires when before select a date.
17995              * @param {Roo.bootstrap.DateField} this
17996              * @param {Mixed} date The date value
17997              */
17998             beforeselect : true
17999         });
18000 };
18001
18002 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18003     
18004     /**
18005      * @cfg {String} format
18006      * The default date format string which can be overriden for localization support.  The format must be
18007      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18008      */
18009     format : "m/d/y",
18010     /**
18011      * @cfg {String} altFormats
18012      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18013      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18014      */
18015     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18016     
18017     weekStart : 0,
18018     
18019     viewMode : '',
18020     
18021     minViewMode : '',
18022     
18023     todayHighlight : false,
18024     
18025     todayBtn: false,
18026     
18027     language: 'en',
18028     
18029     keyboardNavigation: true,
18030     
18031     calendarWeeks: false,
18032     
18033     startDate: -Infinity,
18034     
18035     endDate: Infinity,
18036     
18037     daysOfWeekDisabled: [],
18038     
18039     _events: [],
18040     
18041     singleMode : false,
18042     
18043     UTCDate: function()
18044     {
18045         return new Date(Date.UTC.apply(Date, arguments));
18046     },
18047     
18048     UTCToday: function()
18049     {
18050         var today = new Date();
18051         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18052     },
18053     
18054     getDate: function() {
18055             var d = this.getUTCDate();
18056             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18057     },
18058     
18059     getUTCDate: function() {
18060             return this.date;
18061     },
18062     
18063     setDate: function(d) {
18064             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18065     },
18066     
18067     setUTCDate: function(d) {
18068             this.date = d;
18069             this.setValue(this.formatDate(this.date));
18070     },
18071         
18072     onRender: function(ct, position)
18073     {
18074         
18075         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18076         
18077         this.language = this.language || 'en';
18078         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18079         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18080         
18081         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18082         this.format = this.format || 'm/d/y';
18083         this.isInline = false;
18084         this.isInput = true;
18085         this.component = this.el.select('.add-on', true).first() || false;
18086         this.component = (this.component && this.component.length === 0) ? false : this.component;
18087         this.hasInput = this.component && this.inputEl().length;
18088         
18089         if (typeof(this.minViewMode === 'string')) {
18090             switch (this.minViewMode) {
18091                 case 'months':
18092                     this.minViewMode = 1;
18093                     break;
18094                 case 'years':
18095                     this.minViewMode = 2;
18096                     break;
18097                 default:
18098                     this.minViewMode = 0;
18099                     break;
18100             }
18101         }
18102         
18103         if (typeof(this.viewMode === 'string')) {
18104             switch (this.viewMode) {
18105                 case 'months':
18106                     this.viewMode = 1;
18107                     break;
18108                 case 'years':
18109                     this.viewMode = 2;
18110                     break;
18111                 default:
18112                     this.viewMode = 0;
18113                     break;
18114             }
18115         }
18116                 
18117         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18118         
18119 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18120         
18121         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18122         
18123         this.picker().on('mousedown', this.onMousedown, this);
18124         this.picker().on('click', this.onClick, this);
18125         
18126         this.picker().addClass('datepicker-dropdown');
18127         
18128         this.startViewMode = this.viewMode;
18129         
18130         if(this.singleMode){
18131             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18132                 v.setVisibilityMode(Roo.Element.DISPLAY);
18133                 v.hide();
18134             });
18135             
18136             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18137                 v.setStyle('width', '189px');
18138             });
18139         }
18140         
18141         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18142             if(!this.calendarWeeks){
18143                 v.remove();
18144                 return;
18145             }
18146             
18147             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18148             v.attr('colspan', function(i, val){
18149                 return parseInt(val) + 1;
18150             });
18151         });
18152                         
18153         
18154         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18155         
18156         this.setStartDate(this.startDate);
18157         this.setEndDate(this.endDate);
18158         
18159         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18160         
18161         this.fillDow();
18162         this.fillMonths();
18163         this.update();
18164         this.showMode();
18165         
18166         if(this.isInline) {
18167             this.show();
18168         }
18169     },
18170     
18171     picker : function()
18172     {
18173         return this.pickerEl;
18174 //        return this.el.select('.datepicker', true).first();
18175     },
18176     
18177     fillDow: function()
18178     {
18179         var dowCnt = this.weekStart;
18180         
18181         var dow = {
18182             tag: 'tr',
18183             cn: [
18184                 
18185             ]
18186         };
18187         
18188         if(this.calendarWeeks){
18189             dow.cn.push({
18190                 tag: 'th',
18191                 cls: 'cw',
18192                 html: '&nbsp;'
18193             })
18194         }
18195         
18196         while (dowCnt < this.weekStart + 7) {
18197             dow.cn.push({
18198                 tag: 'th',
18199                 cls: 'dow',
18200                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18201             });
18202         }
18203         
18204         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18205     },
18206     
18207     fillMonths: function()
18208     {    
18209         var i = 0;
18210         var months = this.picker().select('>.datepicker-months td', true).first();
18211         
18212         months.dom.innerHTML = '';
18213         
18214         while (i < 12) {
18215             var month = {
18216                 tag: 'span',
18217                 cls: 'month',
18218                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18219             };
18220             
18221             months.createChild(month);
18222         }
18223         
18224     },
18225     
18226     update: function()
18227     {
18228         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;
18229         
18230         if (this.date < this.startDate) {
18231             this.viewDate = new Date(this.startDate);
18232         } else if (this.date > this.endDate) {
18233             this.viewDate = new Date(this.endDate);
18234         } else {
18235             this.viewDate = new Date(this.date);
18236         }
18237         
18238         this.fill();
18239     },
18240     
18241     fill: function() 
18242     {
18243         var d = new Date(this.viewDate),
18244                 year = d.getUTCFullYear(),
18245                 month = d.getUTCMonth(),
18246                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18247                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18248                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18249                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18250                 currentDate = this.date && this.date.valueOf(),
18251                 today = this.UTCToday();
18252         
18253         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18254         
18255 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18256         
18257 //        this.picker.select('>tfoot th.today').
18258 //                                              .text(dates[this.language].today)
18259 //                                              .toggle(this.todayBtn !== false);
18260     
18261         this.updateNavArrows();
18262         this.fillMonths();
18263                                                 
18264         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18265         
18266         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18267          
18268         prevMonth.setUTCDate(day);
18269         
18270         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18271         
18272         var nextMonth = new Date(prevMonth);
18273         
18274         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18275         
18276         nextMonth = nextMonth.valueOf();
18277         
18278         var fillMonths = false;
18279         
18280         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18281         
18282         while(prevMonth.valueOf() < nextMonth) {
18283             var clsName = '';
18284             
18285             if (prevMonth.getUTCDay() === this.weekStart) {
18286                 if(fillMonths){
18287                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18288                 }
18289                     
18290                 fillMonths = {
18291                     tag: 'tr',
18292                     cn: []
18293                 };
18294                 
18295                 if(this.calendarWeeks){
18296                     // ISO 8601: First week contains first thursday.
18297                     // ISO also states week starts on Monday, but we can be more abstract here.
18298                     var
18299                     // Start of current week: based on weekstart/current date
18300                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18301                     // Thursday of this week
18302                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18303                     // First Thursday of year, year from thursday
18304                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18305                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18306                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18307                     
18308                     fillMonths.cn.push({
18309                         tag: 'td',
18310                         cls: 'cw',
18311                         html: calWeek
18312                     });
18313                 }
18314             }
18315             
18316             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18317                 clsName += ' old';
18318             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18319                 clsName += ' new';
18320             }
18321             if (this.todayHighlight &&
18322                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18323                 prevMonth.getUTCMonth() == today.getMonth() &&
18324                 prevMonth.getUTCDate() == today.getDate()) {
18325                 clsName += ' today';
18326             }
18327             
18328             if (currentDate && prevMonth.valueOf() === currentDate) {
18329                 clsName += ' active';
18330             }
18331             
18332             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18333                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18334                     clsName += ' disabled';
18335             }
18336             
18337             fillMonths.cn.push({
18338                 tag: 'td',
18339                 cls: 'day ' + clsName,
18340                 html: prevMonth.getDate()
18341             });
18342             
18343             prevMonth.setDate(prevMonth.getDate()+1);
18344         }
18345           
18346         var currentYear = this.date && this.date.getUTCFullYear();
18347         var currentMonth = this.date && this.date.getUTCMonth();
18348         
18349         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18350         
18351         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18352             v.removeClass('active');
18353             
18354             if(currentYear === year && k === currentMonth){
18355                 v.addClass('active');
18356             }
18357             
18358             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18359                 v.addClass('disabled');
18360             }
18361             
18362         });
18363         
18364         
18365         year = parseInt(year/10, 10) * 10;
18366         
18367         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18368         
18369         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18370         
18371         year -= 1;
18372         for (var i = -1; i < 11; i++) {
18373             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18374                 tag: 'span',
18375                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18376                 html: year
18377             });
18378             
18379             year += 1;
18380         }
18381     },
18382     
18383     showMode: function(dir) 
18384     {
18385         if (dir) {
18386             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18387         }
18388         
18389         Roo.each(this.picker().select('>div',true).elements, function(v){
18390             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18391             v.hide();
18392         });
18393         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18394     },
18395     
18396     place: function()
18397     {
18398         if(this.isInline) {
18399             return;
18400         }
18401         
18402         this.picker().removeClass(['bottom', 'top']);
18403         
18404         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18405             /*
18406              * place to the top of element!
18407              *
18408              */
18409             
18410             this.picker().addClass('top');
18411             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18412             
18413             return;
18414         }
18415         
18416         this.picker().addClass('bottom');
18417         
18418         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18419     },
18420     
18421     parseDate : function(value)
18422     {
18423         if(!value || value instanceof Date){
18424             return value;
18425         }
18426         var v = Date.parseDate(value, this.format);
18427         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18428             v = Date.parseDate(value, 'Y-m-d');
18429         }
18430         if(!v && this.altFormats){
18431             if(!this.altFormatsArray){
18432                 this.altFormatsArray = this.altFormats.split("|");
18433             }
18434             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18435                 v = Date.parseDate(value, this.altFormatsArray[i]);
18436             }
18437         }
18438         return v;
18439     },
18440     
18441     formatDate : function(date, fmt)
18442     {   
18443         return (!date || !(date instanceof Date)) ?
18444         date : date.dateFormat(fmt || this.format);
18445     },
18446     
18447     onFocus : function()
18448     {
18449         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18450         this.show();
18451     },
18452     
18453     onBlur : function()
18454     {
18455         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18456         
18457         var d = this.inputEl().getValue();
18458         
18459         this.setValue(d);
18460                 
18461         this.hide();
18462     },
18463     
18464     show : function()
18465     {
18466         this.picker().show();
18467         this.update();
18468         this.place();
18469         
18470         this.fireEvent('show', this, this.date);
18471     },
18472     
18473     hide : function()
18474     {
18475         if(this.isInline) {
18476             return;
18477         }
18478         this.picker().hide();
18479         this.viewMode = this.startViewMode;
18480         this.showMode();
18481         
18482         this.fireEvent('hide', this, this.date);
18483         
18484     },
18485     
18486     onMousedown: function(e)
18487     {
18488         e.stopPropagation();
18489         e.preventDefault();
18490     },
18491     
18492     keyup: function(e)
18493     {
18494         Roo.bootstrap.DateField.superclass.keyup.call(this);
18495         this.update();
18496     },
18497
18498     setValue: function(v)
18499     {
18500         if(this.fireEvent('beforeselect', this, v) !== false){
18501             var d = new Date(this.parseDate(v) ).clearTime();
18502         
18503             if(isNaN(d.getTime())){
18504                 this.date = this.viewDate = '';
18505                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18506                 return;
18507             }
18508
18509             v = this.formatDate(d);
18510
18511             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18512
18513             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18514
18515             this.update();
18516
18517             this.fireEvent('select', this, this.date);
18518         }
18519     },
18520     
18521     getValue: function()
18522     {
18523         return this.formatDate(this.date);
18524     },
18525     
18526     fireKey: function(e)
18527     {
18528         if (!this.picker().isVisible()){
18529             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18530                 this.show();
18531             }
18532             return;
18533         }
18534         
18535         var dateChanged = false,
18536         dir, day, month,
18537         newDate, newViewDate;
18538         
18539         switch(e.keyCode){
18540             case 27: // escape
18541                 this.hide();
18542                 e.preventDefault();
18543                 break;
18544             case 37: // left
18545             case 39: // right
18546                 if (!this.keyboardNavigation) {
18547                     break;
18548                 }
18549                 dir = e.keyCode == 37 ? -1 : 1;
18550                 
18551                 if (e.ctrlKey){
18552                     newDate = this.moveYear(this.date, dir);
18553                     newViewDate = this.moveYear(this.viewDate, dir);
18554                 } else if (e.shiftKey){
18555                     newDate = this.moveMonth(this.date, dir);
18556                     newViewDate = this.moveMonth(this.viewDate, dir);
18557                 } else {
18558                     newDate = new Date(this.date);
18559                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18560                     newViewDate = new Date(this.viewDate);
18561                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18562                 }
18563                 if (this.dateWithinRange(newDate)){
18564                     this.date = newDate;
18565                     this.viewDate = newViewDate;
18566                     this.setValue(this.formatDate(this.date));
18567 //                    this.update();
18568                     e.preventDefault();
18569                     dateChanged = true;
18570                 }
18571                 break;
18572             case 38: // up
18573             case 40: // down
18574                 if (!this.keyboardNavigation) {
18575                     break;
18576                 }
18577                 dir = e.keyCode == 38 ? -1 : 1;
18578                 if (e.ctrlKey){
18579                     newDate = this.moveYear(this.date, dir);
18580                     newViewDate = this.moveYear(this.viewDate, dir);
18581                 } else if (e.shiftKey){
18582                     newDate = this.moveMonth(this.date, dir);
18583                     newViewDate = this.moveMonth(this.viewDate, dir);
18584                 } else {
18585                     newDate = new Date(this.date);
18586                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18587                     newViewDate = new Date(this.viewDate);
18588                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18589                 }
18590                 if (this.dateWithinRange(newDate)){
18591                     this.date = newDate;
18592                     this.viewDate = newViewDate;
18593                     this.setValue(this.formatDate(this.date));
18594 //                    this.update();
18595                     e.preventDefault();
18596                     dateChanged = true;
18597                 }
18598                 break;
18599             case 13: // enter
18600                 this.setValue(this.formatDate(this.date));
18601                 this.hide();
18602                 e.preventDefault();
18603                 break;
18604             case 9: // tab
18605                 this.setValue(this.formatDate(this.date));
18606                 this.hide();
18607                 break;
18608             case 16: // shift
18609             case 17: // ctrl
18610             case 18: // alt
18611                 break;
18612             default :
18613                 this.hide();
18614                 
18615         }
18616     },
18617     
18618     
18619     onClick: function(e) 
18620     {
18621         e.stopPropagation();
18622         e.preventDefault();
18623         
18624         var target = e.getTarget();
18625         
18626         if(target.nodeName.toLowerCase() === 'i'){
18627             target = Roo.get(target).dom.parentNode;
18628         }
18629         
18630         var nodeName = target.nodeName;
18631         var className = target.className;
18632         var html = target.innerHTML;
18633         //Roo.log(nodeName);
18634         
18635         switch(nodeName.toLowerCase()) {
18636             case 'th':
18637                 switch(className) {
18638                     case 'switch':
18639                         this.showMode(1);
18640                         break;
18641                     case 'prev':
18642                     case 'next':
18643                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18644                         switch(this.viewMode){
18645                                 case 0:
18646                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18647                                         break;
18648                                 case 1:
18649                                 case 2:
18650                                         this.viewDate = this.moveYear(this.viewDate, dir);
18651                                         break;
18652                         }
18653                         this.fill();
18654                         break;
18655                     case 'today':
18656                         var date = new Date();
18657                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18658 //                        this.fill()
18659                         this.setValue(this.formatDate(this.date));
18660                         
18661                         this.hide();
18662                         break;
18663                 }
18664                 break;
18665             case 'span':
18666                 if (className.indexOf('disabled') < 0) {
18667                     this.viewDate.setUTCDate(1);
18668                     if (className.indexOf('month') > -1) {
18669                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18670                     } else {
18671                         var year = parseInt(html, 10) || 0;
18672                         this.viewDate.setUTCFullYear(year);
18673                         
18674                     }
18675                     
18676                     if(this.singleMode){
18677                         this.setValue(this.formatDate(this.viewDate));
18678                         this.hide();
18679                         return;
18680                     }
18681                     
18682                     this.showMode(-1);
18683                     this.fill();
18684                 }
18685                 break;
18686                 
18687             case 'td':
18688                 //Roo.log(className);
18689                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18690                     var day = parseInt(html, 10) || 1;
18691                     var year = this.viewDate.getUTCFullYear(),
18692                         month = this.viewDate.getUTCMonth();
18693
18694                     if (className.indexOf('old') > -1) {
18695                         if(month === 0 ){
18696                             month = 11;
18697                             year -= 1;
18698                         }else{
18699                             month -= 1;
18700                         }
18701                     } else if (className.indexOf('new') > -1) {
18702                         if (month == 11) {
18703                             month = 0;
18704                             year += 1;
18705                         } else {
18706                             month += 1;
18707                         }
18708                     }
18709                     //Roo.log([year,month,day]);
18710                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18711                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18712 //                    this.fill();
18713                     //Roo.log(this.formatDate(this.date));
18714                     this.setValue(this.formatDate(this.date));
18715                     this.hide();
18716                 }
18717                 break;
18718         }
18719     },
18720     
18721     setStartDate: function(startDate)
18722     {
18723         this.startDate = startDate || -Infinity;
18724         if (this.startDate !== -Infinity) {
18725             this.startDate = this.parseDate(this.startDate);
18726         }
18727         this.update();
18728         this.updateNavArrows();
18729     },
18730
18731     setEndDate: function(endDate)
18732     {
18733         this.endDate = endDate || Infinity;
18734         if (this.endDate !== Infinity) {
18735             this.endDate = this.parseDate(this.endDate);
18736         }
18737         this.update();
18738         this.updateNavArrows();
18739     },
18740     
18741     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18742     {
18743         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18744         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18745             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18746         }
18747         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18748             return parseInt(d, 10);
18749         });
18750         this.update();
18751         this.updateNavArrows();
18752     },
18753     
18754     updateNavArrows: function() 
18755     {
18756         if(this.singleMode){
18757             return;
18758         }
18759         
18760         var d = new Date(this.viewDate),
18761         year = d.getUTCFullYear(),
18762         month = d.getUTCMonth();
18763         
18764         Roo.each(this.picker().select('.prev', true).elements, function(v){
18765             v.show();
18766             switch (this.viewMode) {
18767                 case 0:
18768
18769                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18770                         v.hide();
18771                     }
18772                     break;
18773                 case 1:
18774                 case 2:
18775                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18776                         v.hide();
18777                     }
18778                     break;
18779             }
18780         });
18781         
18782         Roo.each(this.picker().select('.next', true).elements, function(v){
18783             v.show();
18784             switch (this.viewMode) {
18785                 case 0:
18786
18787                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18788                         v.hide();
18789                     }
18790                     break;
18791                 case 1:
18792                 case 2:
18793                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18794                         v.hide();
18795                     }
18796                     break;
18797             }
18798         })
18799     },
18800     
18801     moveMonth: function(date, dir)
18802     {
18803         if (!dir) {
18804             return date;
18805         }
18806         var new_date = new Date(date.valueOf()),
18807         day = new_date.getUTCDate(),
18808         month = new_date.getUTCMonth(),
18809         mag = Math.abs(dir),
18810         new_month, test;
18811         dir = dir > 0 ? 1 : -1;
18812         if (mag == 1){
18813             test = dir == -1
18814             // If going back one month, make sure month is not current month
18815             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18816             ? function(){
18817                 return new_date.getUTCMonth() == month;
18818             }
18819             // If going forward one month, make sure month is as expected
18820             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18821             : function(){
18822                 return new_date.getUTCMonth() != new_month;
18823             };
18824             new_month = month + dir;
18825             new_date.setUTCMonth(new_month);
18826             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18827             if (new_month < 0 || new_month > 11) {
18828                 new_month = (new_month + 12) % 12;
18829             }
18830         } else {
18831             // For magnitudes >1, move one month at a time...
18832             for (var i=0; i<mag; i++) {
18833                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18834                 new_date = this.moveMonth(new_date, dir);
18835             }
18836             // ...then reset the day, keeping it in the new month
18837             new_month = new_date.getUTCMonth();
18838             new_date.setUTCDate(day);
18839             test = function(){
18840                 return new_month != new_date.getUTCMonth();
18841             };
18842         }
18843         // Common date-resetting loop -- if date is beyond end of month, make it
18844         // end of month
18845         while (test()){
18846             new_date.setUTCDate(--day);
18847             new_date.setUTCMonth(new_month);
18848         }
18849         return new_date;
18850     },
18851
18852     moveYear: function(date, dir)
18853     {
18854         return this.moveMonth(date, dir*12);
18855     },
18856
18857     dateWithinRange: function(date)
18858     {
18859         return date >= this.startDate && date <= this.endDate;
18860     },
18861
18862     
18863     remove: function() 
18864     {
18865         this.picker().remove();
18866     },
18867     
18868     validateValue : function(value)
18869     {
18870         if(value.length < 1)  {
18871             if(this.allowBlank){
18872                 return true;
18873             }
18874             return false;
18875         }
18876         
18877         if(value.length < this.minLength){
18878             return false;
18879         }
18880         if(value.length > this.maxLength){
18881             return false;
18882         }
18883         if(this.vtype){
18884             var vt = Roo.form.VTypes;
18885             if(!vt[this.vtype](value, this)){
18886                 return false;
18887             }
18888         }
18889         if(typeof this.validator == "function"){
18890             var msg = this.validator(value);
18891             if(msg !== true){
18892                 return false;
18893             }
18894         }
18895         
18896         if(this.regex && !this.regex.test(value)){
18897             return false;
18898         }
18899         
18900         if(typeof(this.parseDate(value)) == 'undefined'){
18901             return false;
18902         }
18903         
18904         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18905             return false;
18906         }      
18907         
18908         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18909             return false;
18910         } 
18911         
18912         
18913         return true;
18914     }
18915    
18916 });
18917
18918 Roo.apply(Roo.bootstrap.DateField,  {
18919     
18920     head : {
18921         tag: 'thead',
18922         cn: [
18923         {
18924             tag: 'tr',
18925             cn: [
18926             {
18927                 tag: 'th',
18928                 cls: 'prev',
18929                 html: '<i class="fa fa-arrow-left"/>'
18930             },
18931             {
18932                 tag: 'th',
18933                 cls: 'switch',
18934                 colspan: '5'
18935             },
18936             {
18937                 tag: 'th',
18938                 cls: 'next',
18939                 html: '<i class="fa fa-arrow-right"/>'
18940             }
18941
18942             ]
18943         }
18944         ]
18945     },
18946     
18947     content : {
18948         tag: 'tbody',
18949         cn: [
18950         {
18951             tag: 'tr',
18952             cn: [
18953             {
18954                 tag: 'td',
18955                 colspan: '7'
18956             }
18957             ]
18958         }
18959         ]
18960     },
18961     
18962     footer : {
18963         tag: 'tfoot',
18964         cn: [
18965         {
18966             tag: 'tr',
18967             cn: [
18968             {
18969                 tag: 'th',
18970                 colspan: '7',
18971                 cls: 'today'
18972             }
18973                     
18974             ]
18975         }
18976         ]
18977     },
18978     
18979     dates:{
18980         en: {
18981             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18982             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18983             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18984             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18985             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18986             today: "Today"
18987         }
18988     },
18989     
18990     modes: [
18991     {
18992         clsName: 'days',
18993         navFnc: 'Month',
18994         navStep: 1
18995     },
18996     {
18997         clsName: 'months',
18998         navFnc: 'FullYear',
18999         navStep: 1
19000     },
19001     {
19002         clsName: 'years',
19003         navFnc: 'FullYear',
19004         navStep: 10
19005     }]
19006 });
19007
19008 Roo.apply(Roo.bootstrap.DateField,  {
19009   
19010     template : {
19011         tag: 'div',
19012         cls: 'datepicker dropdown-menu roo-dynamic',
19013         cn: [
19014         {
19015             tag: 'div',
19016             cls: 'datepicker-days',
19017             cn: [
19018             {
19019                 tag: 'table',
19020                 cls: 'table-condensed',
19021                 cn:[
19022                 Roo.bootstrap.DateField.head,
19023                 {
19024                     tag: 'tbody'
19025                 },
19026                 Roo.bootstrap.DateField.footer
19027                 ]
19028             }
19029             ]
19030         },
19031         {
19032             tag: 'div',
19033             cls: 'datepicker-months',
19034             cn: [
19035             {
19036                 tag: 'table',
19037                 cls: 'table-condensed',
19038                 cn:[
19039                 Roo.bootstrap.DateField.head,
19040                 Roo.bootstrap.DateField.content,
19041                 Roo.bootstrap.DateField.footer
19042                 ]
19043             }
19044             ]
19045         },
19046         {
19047             tag: 'div',
19048             cls: 'datepicker-years',
19049             cn: [
19050             {
19051                 tag: 'table',
19052                 cls: 'table-condensed',
19053                 cn:[
19054                 Roo.bootstrap.DateField.head,
19055                 Roo.bootstrap.DateField.content,
19056                 Roo.bootstrap.DateField.footer
19057                 ]
19058             }
19059             ]
19060         }
19061         ]
19062     }
19063 });
19064
19065  
19066
19067  /*
19068  * - LGPL
19069  *
19070  * TimeField
19071  * 
19072  */
19073
19074 /**
19075  * @class Roo.bootstrap.TimeField
19076  * @extends Roo.bootstrap.Input
19077  * Bootstrap DateField class
19078  * 
19079  * 
19080  * @constructor
19081  * Create a new TimeField
19082  * @param {Object} config The config object
19083  */
19084
19085 Roo.bootstrap.TimeField = function(config){
19086     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19087     this.addEvents({
19088             /**
19089              * @event show
19090              * Fires when this field show.
19091              * @param {Roo.bootstrap.DateField} thisthis
19092              * @param {Mixed} date The date value
19093              */
19094             show : true,
19095             /**
19096              * @event show
19097              * Fires when this field hide.
19098              * @param {Roo.bootstrap.DateField} this
19099              * @param {Mixed} date The date value
19100              */
19101             hide : true,
19102             /**
19103              * @event select
19104              * Fires when select a date.
19105              * @param {Roo.bootstrap.DateField} this
19106              * @param {Mixed} date The date value
19107              */
19108             select : true
19109         });
19110 };
19111
19112 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19113     
19114     /**
19115      * @cfg {String} format
19116      * The default time format string which can be overriden for localization support.  The format must be
19117      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19118      */
19119     format : "H:i",
19120        
19121     onRender: function(ct, position)
19122     {
19123         
19124         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19125                 
19126         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19127         
19128         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19129         
19130         this.pop = this.picker().select('>.datepicker-time',true).first();
19131         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19132         
19133         this.picker().on('mousedown', this.onMousedown, this);
19134         this.picker().on('click', this.onClick, this);
19135         
19136         this.picker().addClass('datepicker-dropdown');
19137     
19138         this.fillTime();
19139         this.update();
19140             
19141         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19142         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19143         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19144         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19145         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19146         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19147
19148     },
19149     
19150     fireKey: function(e){
19151         if (!this.picker().isVisible()){
19152             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19153                 this.show();
19154             }
19155             return;
19156         }
19157
19158         e.preventDefault();
19159         
19160         switch(e.keyCode){
19161             case 27: // escape
19162                 this.hide();
19163                 break;
19164             case 37: // left
19165             case 39: // right
19166                 this.onTogglePeriod();
19167                 break;
19168             case 38: // up
19169                 this.onIncrementMinutes();
19170                 break;
19171             case 40: // down
19172                 this.onDecrementMinutes();
19173                 break;
19174             case 13: // enter
19175             case 9: // tab
19176                 this.setTime();
19177                 break;
19178         }
19179     },
19180     
19181     onClick: function(e) {
19182         e.stopPropagation();
19183         e.preventDefault();
19184     },
19185     
19186     picker : function()
19187     {
19188         return this.el.select('.datepicker', true).first();
19189     },
19190     
19191     fillTime: function()
19192     {    
19193         var time = this.pop.select('tbody', true).first();
19194         
19195         time.dom.innerHTML = '';
19196         
19197         time.createChild({
19198             tag: 'tr',
19199             cn: [
19200                 {
19201                     tag: 'td',
19202                     cn: [
19203                         {
19204                             tag: 'a',
19205                             href: '#',
19206                             cls: 'btn',
19207                             cn: [
19208                                 {
19209                                     tag: 'span',
19210                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19211                                 }
19212                             ]
19213                         } 
19214                     ]
19215                 },
19216                 {
19217                     tag: 'td',
19218                     cls: 'separator'
19219                 },
19220                 {
19221                     tag: 'td',
19222                     cn: [
19223                         {
19224                             tag: 'a',
19225                             href: '#',
19226                             cls: 'btn',
19227                             cn: [
19228                                 {
19229                                     tag: 'span',
19230                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19231                                 }
19232                             ]
19233                         }
19234                     ]
19235                 },
19236                 {
19237                     tag: 'td',
19238                     cls: 'separator'
19239                 }
19240             ]
19241         });
19242         
19243         time.createChild({
19244             tag: 'tr',
19245             cn: [
19246                 {
19247                     tag: 'td',
19248                     cn: [
19249                         {
19250                             tag: 'span',
19251                             cls: 'timepicker-hour',
19252                             html: '00'
19253                         }  
19254                     ]
19255                 },
19256                 {
19257                     tag: 'td',
19258                     cls: 'separator',
19259                     html: ':'
19260                 },
19261                 {
19262                     tag: 'td',
19263                     cn: [
19264                         {
19265                             tag: 'span',
19266                             cls: 'timepicker-minute',
19267                             html: '00'
19268                         }  
19269                     ]
19270                 },
19271                 {
19272                     tag: 'td',
19273                     cls: 'separator'
19274                 },
19275                 {
19276                     tag: 'td',
19277                     cn: [
19278                         {
19279                             tag: 'button',
19280                             type: 'button',
19281                             cls: 'btn btn-primary period',
19282                             html: 'AM'
19283                             
19284                         }
19285                     ]
19286                 }
19287             ]
19288         });
19289         
19290         time.createChild({
19291             tag: 'tr',
19292             cn: [
19293                 {
19294                     tag: 'td',
19295                     cn: [
19296                         {
19297                             tag: 'a',
19298                             href: '#',
19299                             cls: 'btn',
19300                             cn: [
19301                                 {
19302                                     tag: 'span',
19303                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19304                                 }
19305                             ]
19306                         }
19307                     ]
19308                 },
19309                 {
19310                     tag: 'td',
19311                     cls: 'separator'
19312                 },
19313                 {
19314                     tag: 'td',
19315                     cn: [
19316                         {
19317                             tag: 'a',
19318                             href: '#',
19319                             cls: 'btn',
19320                             cn: [
19321                                 {
19322                                     tag: 'span',
19323                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19324                                 }
19325                             ]
19326                         }
19327                     ]
19328                 },
19329                 {
19330                     tag: 'td',
19331                     cls: 'separator'
19332                 }
19333             ]
19334         });
19335         
19336     },
19337     
19338     update: function()
19339     {
19340         
19341         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19342         
19343         this.fill();
19344     },
19345     
19346     fill: function() 
19347     {
19348         var hours = this.time.getHours();
19349         var minutes = this.time.getMinutes();
19350         var period = 'AM';
19351         
19352         if(hours > 11){
19353             period = 'PM';
19354         }
19355         
19356         if(hours == 0){
19357             hours = 12;
19358         }
19359         
19360         
19361         if(hours > 12){
19362             hours = hours - 12;
19363         }
19364         
19365         if(hours < 10){
19366             hours = '0' + hours;
19367         }
19368         
19369         if(minutes < 10){
19370             minutes = '0' + minutes;
19371         }
19372         
19373         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19374         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19375         this.pop.select('button', true).first().dom.innerHTML = period;
19376         
19377     },
19378     
19379     place: function()
19380     {   
19381         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19382         
19383         var cls = ['bottom'];
19384         
19385         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19386             cls.pop();
19387             cls.push('top');
19388         }
19389         
19390         cls.push('right');
19391         
19392         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19393             cls.pop();
19394             cls.push('left');
19395         }
19396         
19397         this.picker().addClass(cls.join('-'));
19398         
19399         var _this = this;
19400         
19401         Roo.each(cls, function(c){
19402             if(c == 'bottom'){
19403                 _this.picker().setTop(_this.inputEl().getHeight());
19404                 return;
19405             }
19406             if(c == 'top'){
19407                 _this.picker().setTop(0 - _this.picker().getHeight());
19408                 return;
19409             }
19410             
19411             if(c == 'left'){
19412                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19413                 return;
19414             }
19415             if(c == 'right'){
19416                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19417                 return;
19418             }
19419         });
19420         
19421     },
19422   
19423     onFocus : function()
19424     {
19425         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19426         this.show();
19427     },
19428     
19429     onBlur : function()
19430     {
19431         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19432         this.hide();
19433     },
19434     
19435     show : function()
19436     {
19437         this.picker().show();
19438         this.pop.show();
19439         this.update();
19440         this.place();
19441         
19442         this.fireEvent('show', this, this.date);
19443     },
19444     
19445     hide : function()
19446     {
19447         this.picker().hide();
19448         this.pop.hide();
19449         
19450         this.fireEvent('hide', this, this.date);
19451     },
19452     
19453     setTime : function()
19454     {
19455         this.hide();
19456         this.setValue(this.time.format(this.format));
19457         
19458         this.fireEvent('select', this, this.date);
19459         
19460         
19461     },
19462     
19463     onMousedown: function(e){
19464         e.stopPropagation();
19465         e.preventDefault();
19466     },
19467     
19468     onIncrementHours: function()
19469     {
19470         Roo.log('onIncrementHours');
19471         this.time = this.time.add(Date.HOUR, 1);
19472         this.update();
19473         
19474     },
19475     
19476     onDecrementHours: function()
19477     {
19478         Roo.log('onDecrementHours');
19479         this.time = this.time.add(Date.HOUR, -1);
19480         this.update();
19481     },
19482     
19483     onIncrementMinutes: function()
19484     {
19485         Roo.log('onIncrementMinutes');
19486         this.time = this.time.add(Date.MINUTE, 1);
19487         this.update();
19488     },
19489     
19490     onDecrementMinutes: function()
19491     {
19492         Roo.log('onDecrementMinutes');
19493         this.time = this.time.add(Date.MINUTE, -1);
19494         this.update();
19495     },
19496     
19497     onTogglePeriod: function()
19498     {
19499         Roo.log('onTogglePeriod');
19500         this.time = this.time.add(Date.HOUR, 12);
19501         this.update();
19502     }
19503     
19504    
19505 });
19506
19507 Roo.apply(Roo.bootstrap.TimeField,  {
19508     
19509     content : {
19510         tag: 'tbody',
19511         cn: [
19512             {
19513                 tag: 'tr',
19514                 cn: [
19515                 {
19516                     tag: 'td',
19517                     colspan: '7'
19518                 }
19519                 ]
19520             }
19521         ]
19522     },
19523     
19524     footer : {
19525         tag: 'tfoot',
19526         cn: [
19527             {
19528                 tag: 'tr',
19529                 cn: [
19530                 {
19531                     tag: 'th',
19532                     colspan: '7',
19533                     cls: '',
19534                     cn: [
19535                         {
19536                             tag: 'button',
19537                             cls: 'btn btn-info ok',
19538                             html: 'OK'
19539                         }
19540                     ]
19541                 }
19542
19543                 ]
19544             }
19545         ]
19546     }
19547 });
19548
19549 Roo.apply(Roo.bootstrap.TimeField,  {
19550   
19551     template : {
19552         tag: 'div',
19553         cls: 'datepicker dropdown-menu',
19554         cn: [
19555             {
19556                 tag: 'div',
19557                 cls: 'datepicker-time',
19558                 cn: [
19559                 {
19560                     tag: 'table',
19561                     cls: 'table-condensed',
19562                     cn:[
19563                     Roo.bootstrap.TimeField.content,
19564                     Roo.bootstrap.TimeField.footer
19565                     ]
19566                 }
19567                 ]
19568             }
19569         ]
19570     }
19571 });
19572
19573  
19574
19575  /*
19576  * - LGPL
19577  *
19578  * MonthField
19579  * 
19580  */
19581
19582 /**
19583  * @class Roo.bootstrap.MonthField
19584  * @extends Roo.bootstrap.Input
19585  * Bootstrap MonthField class
19586  * 
19587  * @cfg {String} language default en
19588  * 
19589  * @constructor
19590  * Create a new MonthField
19591  * @param {Object} config The config object
19592  */
19593
19594 Roo.bootstrap.MonthField = function(config){
19595     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19596     
19597     this.addEvents({
19598         /**
19599          * @event show
19600          * Fires when this field show.
19601          * @param {Roo.bootstrap.MonthField} this
19602          * @param {Mixed} date The date value
19603          */
19604         show : true,
19605         /**
19606          * @event show
19607          * Fires when this field hide.
19608          * @param {Roo.bootstrap.MonthField} this
19609          * @param {Mixed} date The date value
19610          */
19611         hide : true,
19612         /**
19613          * @event select
19614          * Fires when select a date.
19615          * @param {Roo.bootstrap.MonthField} this
19616          * @param {String} oldvalue The old value
19617          * @param {String} newvalue The new value
19618          */
19619         select : true
19620     });
19621 };
19622
19623 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19624     
19625     onRender: function(ct, position)
19626     {
19627         
19628         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19629         
19630         this.language = this.language || 'en';
19631         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19632         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19633         
19634         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19635         this.isInline = false;
19636         this.isInput = true;
19637         this.component = this.el.select('.add-on', true).first() || false;
19638         this.component = (this.component && this.component.length === 0) ? false : this.component;
19639         this.hasInput = this.component && this.inputEL().length;
19640         
19641         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19642         
19643         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19644         
19645         this.picker().on('mousedown', this.onMousedown, this);
19646         this.picker().on('click', this.onClick, this);
19647         
19648         this.picker().addClass('datepicker-dropdown');
19649         
19650         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19651             v.setStyle('width', '189px');
19652         });
19653         
19654         this.fillMonths();
19655         
19656         this.update();
19657         
19658         if(this.isInline) {
19659             this.show();
19660         }
19661         
19662     },
19663     
19664     setValue: function(v, suppressEvent)
19665     {   
19666         var o = this.getValue();
19667         
19668         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19669         
19670         this.update();
19671
19672         if(suppressEvent !== true){
19673             this.fireEvent('select', this, o, v);
19674         }
19675         
19676     },
19677     
19678     getValue: function()
19679     {
19680         return this.value;
19681     },
19682     
19683     onClick: function(e) 
19684     {
19685         e.stopPropagation();
19686         e.preventDefault();
19687         
19688         var target = e.getTarget();
19689         
19690         if(target.nodeName.toLowerCase() === 'i'){
19691             target = Roo.get(target).dom.parentNode;
19692         }
19693         
19694         var nodeName = target.nodeName;
19695         var className = target.className;
19696         var html = target.innerHTML;
19697         
19698         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19699             return;
19700         }
19701         
19702         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19703         
19704         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19705         
19706         this.hide();
19707                         
19708     },
19709     
19710     picker : function()
19711     {
19712         return this.pickerEl;
19713     },
19714     
19715     fillMonths: function()
19716     {    
19717         var i = 0;
19718         var months = this.picker().select('>.datepicker-months td', true).first();
19719         
19720         months.dom.innerHTML = '';
19721         
19722         while (i < 12) {
19723             var month = {
19724                 tag: 'span',
19725                 cls: 'month',
19726                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19727             };
19728             
19729             months.createChild(month);
19730         }
19731         
19732     },
19733     
19734     update: function()
19735     {
19736         var _this = this;
19737         
19738         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19739             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19740         }
19741         
19742         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19743             e.removeClass('active');
19744             
19745             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19746                 e.addClass('active');
19747             }
19748         })
19749     },
19750     
19751     place: function()
19752     {
19753         if(this.isInline) {
19754             return;
19755         }
19756         
19757         this.picker().removeClass(['bottom', 'top']);
19758         
19759         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19760             /*
19761              * place to the top of element!
19762              *
19763              */
19764             
19765             this.picker().addClass('top');
19766             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19767             
19768             return;
19769         }
19770         
19771         this.picker().addClass('bottom');
19772         
19773         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19774     },
19775     
19776     onFocus : function()
19777     {
19778         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19779         this.show();
19780     },
19781     
19782     onBlur : function()
19783     {
19784         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19785         
19786         var d = this.inputEl().getValue();
19787         
19788         this.setValue(d);
19789                 
19790         this.hide();
19791     },
19792     
19793     show : function()
19794     {
19795         this.picker().show();
19796         this.picker().select('>.datepicker-months', true).first().show();
19797         this.update();
19798         this.place();
19799         
19800         this.fireEvent('show', this, this.date);
19801     },
19802     
19803     hide : function()
19804     {
19805         if(this.isInline) {
19806             return;
19807         }
19808         this.picker().hide();
19809         this.fireEvent('hide', this, this.date);
19810         
19811     },
19812     
19813     onMousedown: function(e)
19814     {
19815         e.stopPropagation();
19816         e.preventDefault();
19817     },
19818     
19819     keyup: function(e)
19820     {
19821         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19822         this.update();
19823     },
19824
19825     fireKey: function(e)
19826     {
19827         if (!this.picker().isVisible()){
19828             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19829                 this.show();
19830             }
19831             return;
19832         }
19833         
19834         var dir;
19835         
19836         switch(e.keyCode){
19837             case 27: // escape
19838                 this.hide();
19839                 e.preventDefault();
19840                 break;
19841             case 37: // left
19842             case 39: // right
19843                 dir = e.keyCode == 37 ? -1 : 1;
19844                 
19845                 this.vIndex = this.vIndex + dir;
19846                 
19847                 if(this.vIndex < 0){
19848                     this.vIndex = 0;
19849                 }
19850                 
19851                 if(this.vIndex > 11){
19852                     this.vIndex = 11;
19853                 }
19854                 
19855                 if(isNaN(this.vIndex)){
19856                     this.vIndex = 0;
19857                 }
19858                 
19859                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19860                 
19861                 break;
19862             case 38: // up
19863             case 40: // down
19864                 
19865                 dir = e.keyCode == 38 ? -1 : 1;
19866                 
19867                 this.vIndex = this.vIndex + dir * 4;
19868                 
19869                 if(this.vIndex < 0){
19870                     this.vIndex = 0;
19871                 }
19872                 
19873                 if(this.vIndex > 11){
19874                     this.vIndex = 11;
19875                 }
19876                 
19877                 if(isNaN(this.vIndex)){
19878                     this.vIndex = 0;
19879                 }
19880                 
19881                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19882                 break;
19883                 
19884             case 13: // enter
19885                 
19886                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19887                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19888                 }
19889                 
19890                 this.hide();
19891                 e.preventDefault();
19892                 break;
19893             case 9: // tab
19894                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19895                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19896                 }
19897                 this.hide();
19898                 break;
19899             case 16: // shift
19900             case 17: // ctrl
19901             case 18: // alt
19902                 break;
19903             default :
19904                 this.hide();
19905                 
19906         }
19907     },
19908     
19909     remove: function() 
19910     {
19911         this.picker().remove();
19912     }
19913    
19914 });
19915
19916 Roo.apply(Roo.bootstrap.MonthField,  {
19917     
19918     content : {
19919         tag: 'tbody',
19920         cn: [
19921         {
19922             tag: 'tr',
19923             cn: [
19924             {
19925                 tag: 'td',
19926                 colspan: '7'
19927             }
19928             ]
19929         }
19930         ]
19931     },
19932     
19933     dates:{
19934         en: {
19935             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19936             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19937         }
19938     }
19939 });
19940
19941 Roo.apply(Roo.bootstrap.MonthField,  {
19942   
19943     template : {
19944         tag: 'div',
19945         cls: 'datepicker dropdown-menu roo-dynamic',
19946         cn: [
19947             {
19948                 tag: 'div',
19949                 cls: 'datepicker-months',
19950                 cn: [
19951                 {
19952                     tag: 'table',
19953                     cls: 'table-condensed',
19954                     cn:[
19955                         Roo.bootstrap.DateField.content
19956                     ]
19957                 }
19958                 ]
19959             }
19960         ]
19961     }
19962 });
19963
19964  
19965
19966  
19967  /*
19968  * - LGPL
19969  *
19970  * CheckBox
19971  * 
19972  */
19973
19974 /**
19975  * @class Roo.bootstrap.CheckBox
19976  * @extends Roo.bootstrap.Input
19977  * Bootstrap CheckBox class
19978  * 
19979  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19980  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19981  * @cfg {String} boxLabel The text that appears beside the checkbox
19982  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19983  * @cfg {Boolean} checked initnal the element
19984  * @cfg {Boolean} inline inline the element (default false)
19985  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19986  * 
19987  * @constructor
19988  * Create a new CheckBox
19989  * @param {Object} config The config object
19990  */
19991
19992 Roo.bootstrap.CheckBox = function(config){
19993     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19994    
19995     this.addEvents({
19996         /**
19997         * @event check
19998         * Fires when the element is checked or unchecked.
19999         * @param {Roo.bootstrap.CheckBox} this This input
20000         * @param {Boolean} checked The new checked value
20001         */
20002        check : true
20003     });
20004     
20005 };
20006
20007 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20008   
20009     inputType: 'checkbox',
20010     inputValue: 1,
20011     valueOff: 0,
20012     boxLabel: false,
20013     checked: false,
20014     weight : false,
20015     inline: false,
20016     
20017     getAutoCreate : function()
20018     {
20019         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20020         
20021         var id = Roo.id();
20022         
20023         var cfg = {};
20024         
20025         cfg.cls = 'form-group ' + this.inputType; //input-group
20026         
20027         if(this.inline){
20028             cfg.cls += ' ' + this.inputType + '-inline';
20029         }
20030         
20031         var input =  {
20032             tag: 'input',
20033             id : id,
20034             type : this.inputType,
20035             value : this.inputValue,
20036             cls : 'roo-' + this.inputType, //'form-box',
20037             placeholder : this.placeholder || ''
20038             
20039         };
20040         
20041         if(this.inputType != 'radio'){
20042             var hidden =  {
20043                 tag: 'input',
20044                 type : 'hidden',
20045                 cls : 'roo-hidden-value',
20046                 value : this.checked ? this.valueOff : this.inputValue
20047             };
20048         }
20049         
20050             
20051         if (this.weight) { // Validity check?
20052             cfg.cls += " " + this.inputType + "-" + this.weight;
20053         }
20054         
20055         if (this.disabled) {
20056             input.disabled=true;
20057         }
20058         
20059         if(this.checked){
20060             input.checked = this.checked;
20061             
20062         }
20063         
20064         
20065         if (this.name) {
20066             
20067             input.name = this.name;
20068             
20069             if(this.inputType != 'radio'){
20070                 hidden.name = this.name;
20071                 input.name = '_hidden_' + this.name;
20072             }
20073         }
20074         
20075         if (this.size) {
20076             input.cls += ' input-' + this.size;
20077         }
20078         
20079         var settings=this;
20080         
20081         ['xs','sm','md','lg'].map(function(size){
20082             if (settings[size]) {
20083                 cfg.cls += ' col-' + size + '-' + settings[size];
20084             }
20085         });
20086         
20087         var inputblock = input;
20088          
20089         if (this.before || this.after) {
20090             
20091             inputblock = {
20092                 cls : 'input-group',
20093                 cn :  [] 
20094             };
20095             
20096             if (this.before) {
20097                 inputblock.cn.push({
20098                     tag :'span',
20099                     cls : 'input-group-addon',
20100                     html : this.before
20101                 });
20102             }
20103             
20104             inputblock.cn.push(input);
20105             
20106             if(this.inputType != 'radio'){
20107                 inputblock.cn.push(hidden);
20108             }
20109             
20110             if (this.after) {
20111                 inputblock.cn.push({
20112                     tag :'span',
20113                     cls : 'input-group-addon',
20114                     html : this.after
20115                 });
20116             }
20117             
20118         }
20119         
20120         if (align ==='left' && this.fieldLabel.length) {
20121 //                Roo.log("left and has label");
20122             cfg.cn = [
20123                 {
20124                     tag: 'label',
20125                     'for' :  id,
20126                     cls : 'control-label',
20127                     html : this.fieldLabel
20128
20129                 },
20130                 {
20131                     cls : "", 
20132                     cn: [
20133                         inputblock
20134                     ]
20135                 }
20136             ];
20137             
20138             if(this.labelWidth > 12){
20139                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20140             }
20141             
20142             if(this.labelWidth < 13 && this.labelmd == 0){
20143                 this.labelmd = this.labelWidth;
20144             }
20145             
20146             if(this.labellg > 0){
20147                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20148                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20149             }
20150             
20151             if(this.labelmd > 0){
20152                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20153                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20154             }
20155             
20156             if(this.labelsm > 0){
20157                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20158                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20159             }
20160             
20161             if(this.labelxs > 0){
20162                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20163                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20164             }
20165             
20166         } else if ( this.fieldLabel.length) {
20167 //                Roo.log(" label");
20168                 cfg.cn = [
20169                    
20170                     {
20171                         tag: this.boxLabel ? 'span' : 'label',
20172                         'for': id,
20173                         cls: 'control-label box-input-label',
20174                         //cls : 'input-group-addon',
20175                         html : this.fieldLabel
20176                         
20177                     },
20178                     
20179                     inputblock
20180                     
20181                 ];
20182
20183         } else {
20184             
20185 //                Roo.log(" no label && no align");
20186                 cfg.cn = [  inputblock ] ;
20187                 
20188                 
20189         }
20190         
20191         if(this.boxLabel){
20192              var boxLabelCfg = {
20193                 tag: 'label',
20194                 //'for': id, // box label is handled by onclick - so no for...
20195                 cls: 'box-label',
20196                 html: this.boxLabel
20197             };
20198             
20199             if(this.tooltip){
20200                 boxLabelCfg.tooltip = this.tooltip;
20201             }
20202              
20203             cfg.cn.push(boxLabelCfg);
20204         }
20205         
20206         if(this.inputType != 'radio'){
20207             cfg.cn.push(hidden);
20208         }
20209         
20210         return cfg;
20211         
20212     },
20213     
20214     /**
20215      * return the real input element.
20216      */
20217     inputEl: function ()
20218     {
20219         return this.el.select('input.roo-' + this.inputType,true).first();
20220     },
20221     hiddenEl: function ()
20222     {
20223         return this.el.select('input.roo-hidden-value',true).first();
20224     },
20225     
20226     labelEl: function()
20227     {
20228         return this.el.select('label.control-label',true).first();
20229     },
20230     /* depricated... */
20231     
20232     label: function()
20233     {
20234         return this.labelEl();
20235     },
20236     
20237     boxLabelEl: function()
20238     {
20239         return this.el.select('label.box-label',true).first();
20240     },
20241     
20242     initEvents : function()
20243     {
20244 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20245         
20246         this.inputEl().on('click', this.onClick,  this);
20247         
20248         if (this.boxLabel) { 
20249             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20250         }
20251         
20252         this.startValue = this.getValue();
20253         
20254         if(this.groupId){
20255             Roo.bootstrap.CheckBox.register(this);
20256         }
20257     },
20258     
20259     onClick : function()
20260     {   
20261         this.setChecked(!this.checked);
20262     },
20263     
20264     setChecked : function(state,suppressEvent)
20265     {
20266         this.startValue = this.getValue();
20267
20268         if(this.inputType == 'radio'){
20269             
20270             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20271                 e.dom.checked = false;
20272             });
20273             
20274             this.inputEl().dom.checked = true;
20275             
20276             this.inputEl().dom.value = this.inputValue;
20277             
20278             if(suppressEvent !== true){
20279                 this.fireEvent('check', this, true);
20280             }
20281             
20282             this.validate();
20283             
20284             return;
20285         }
20286         
20287         this.checked = state;
20288         
20289         this.inputEl().dom.checked = state;
20290         
20291         
20292         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20293         
20294         if(suppressEvent !== true){
20295             this.fireEvent('check', this, state);
20296         }
20297         
20298         this.validate();
20299     },
20300     
20301     getValue : function()
20302     {
20303         if(this.inputType == 'radio'){
20304             return this.getGroupValue();
20305         }
20306         
20307         return this.hiddenEl().dom.value;
20308         
20309     },
20310     
20311     getGroupValue : function()
20312     {
20313         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20314             return '';
20315         }
20316         
20317         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20318     },
20319     
20320     setValue : function(v,suppressEvent)
20321     {
20322         if(this.inputType == 'radio'){
20323             this.setGroupValue(v, suppressEvent);
20324             return;
20325         }
20326         
20327         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20328         
20329         this.validate();
20330     },
20331     
20332     setGroupValue : function(v, suppressEvent)
20333     {
20334         this.startValue = this.getValue();
20335         
20336         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20337             e.dom.checked = false;
20338             
20339             if(e.dom.value == v){
20340                 e.dom.checked = true;
20341             }
20342         });
20343         
20344         if(suppressEvent !== true){
20345             this.fireEvent('check', this, true);
20346         }
20347
20348         this.validate();
20349         
20350         return;
20351     },
20352     
20353     validate : function()
20354     {
20355         if(
20356                 this.disabled || 
20357                 (this.inputType == 'radio' && this.validateRadio()) ||
20358                 (this.inputType == 'checkbox' && this.validateCheckbox())
20359         ){
20360             this.markValid();
20361             return true;
20362         }
20363         
20364         this.markInvalid();
20365         return false;
20366     },
20367     
20368     validateRadio : function()
20369     {
20370         if(this.allowBlank){
20371             return true;
20372         }
20373         
20374         var valid = false;
20375         
20376         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20377             if(!e.dom.checked){
20378                 return;
20379             }
20380             
20381             valid = true;
20382             
20383             return false;
20384         });
20385         
20386         return valid;
20387     },
20388     
20389     validateCheckbox : function()
20390     {
20391         if(!this.groupId){
20392             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20393             //return (this.getValue() == this.inputValue) ? true : false;
20394         }
20395         
20396         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20397         
20398         if(!group){
20399             return false;
20400         }
20401         
20402         var r = false;
20403         
20404         for(var i in group){
20405             if(r){
20406                 break;
20407             }
20408             
20409             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20410         }
20411         
20412         return r;
20413     },
20414     
20415     /**
20416      * Mark this field as valid
20417      */
20418     markValid : function()
20419     {
20420         var _this = this;
20421         
20422         this.fireEvent('valid', this);
20423         
20424         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20425         
20426         if(this.groupId){
20427             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20428         }
20429         
20430         if(label){
20431             label.markValid();
20432         }
20433
20434         if(this.inputType == 'radio'){
20435             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20436                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20437                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20438             });
20439             
20440             return;
20441         }
20442
20443         if(!this.groupId){
20444             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20445             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20446             return;
20447         }
20448         
20449         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20450         
20451         if(!group){
20452             return;
20453         }
20454         
20455         for(var i in group){
20456             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20457             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20458         }
20459     },
20460     
20461      /**
20462      * Mark this field as invalid
20463      * @param {String} msg The validation message
20464      */
20465     markInvalid : function(msg)
20466     {
20467         if(this.allowBlank){
20468             return;
20469         }
20470         
20471         var _this = this;
20472         
20473         this.fireEvent('invalid', this, msg);
20474         
20475         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20476         
20477         if(this.groupId){
20478             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20479         }
20480         
20481         if(label){
20482             label.markInvalid();
20483         }
20484             
20485         if(this.inputType == 'radio'){
20486             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20487                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20488                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20489             });
20490             
20491             return;
20492         }
20493         
20494         if(!this.groupId){
20495             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20496             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20497             return;
20498         }
20499         
20500         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20501         
20502         if(!group){
20503             return;
20504         }
20505         
20506         for(var i in group){
20507             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20508             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20509         }
20510         
20511     },
20512     
20513     clearInvalid : function()
20514     {
20515         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20516         
20517         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20518         
20519         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20520         
20521         if (label) {
20522             label.iconEl.removeClass(label.validClass);
20523             label.iconEl.removeClass(label.invalidClass);
20524         }
20525     },
20526     
20527     disable : function()
20528     {
20529         if(this.inputType != 'radio'){
20530             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20531             return;
20532         }
20533         
20534         var _this = this;
20535         
20536         if(this.rendered){
20537             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20538                 _this.getActionEl().addClass(this.disabledClass);
20539                 e.dom.disabled = true;
20540             });
20541         }
20542         
20543         this.disabled = true;
20544         this.fireEvent("disable", this);
20545         return this;
20546     },
20547
20548     enable : function()
20549     {
20550         if(this.inputType != 'radio'){
20551             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20552             return;
20553         }
20554         
20555         var _this = this;
20556         
20557         if(this.rendered){
20558             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20559                 _this.getActionEl().removeClass(this.disabledClass);
20560                 e.dom.disabled = false;
20561             });
20562         }
20563         
20564         this.disabled = false;
20565         this.fireEvent("enable", this);
20566         return this;
20567     }
20568
20569 });
20570
20571 Roo.apply(Roo.bootstrap.CheckBox, {
20572     
20573     groups: {},
20574     
20575      /**
20576     * register a CheckBox Group
20577     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20578     */
20579     register : function(checkbox)
20580     {
20581         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20582             this.groups[checkbox.groupId] = {};
20583         }
20584         
20585         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20586             return;
20587         }
20588         
20589         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20590         
20591     },
20592     /**
20593     * fetch a CheckBox Group based on the group ID
20594     * @param {string} the group ID
20595     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20596     */
20597     get: function(groupId) {
20598         if (typeof(this.groups[groupId]) == 'undefined') {
20599             return false;
20600         }
20601         
20602         return this.groups[groupId] ;
20603     }
20604     
20605     
20606 });
20607 /*
20608  * - LGPL
20609  *
20610  * RadioItem
20611  * 
20612  */
20613
20614 /**
20615  * @class Roo.bootstrap.Radio
20616  * @extends Roo.bootstrap.Component
20617  * Bootstrap Radio class
20618  * @cfg {String} boxLabel - the label associated
20619  * @cfg {String} value - the value of radio
20620  * 
20621  * @constructor
20622  * Create a new Radio
20623  * @param {Object} config The config object
20624  */
20625 Roo.bootstrap.Radio = function(config){
20626     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20627     
20628 };
20629
20630 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20631     
20632     boxLabel : '',
20633     
20634     value : '',
20635     
20636     getAutoCreate : function()
20637     {
20638         var cfg = {
20639             tag : 'div',
20640             cls : 'form-group radio',
20641             cn : [
20642                 {
20643                     tag : 'label',
20644                     cls : 'box-label',
20645                     html : this.boxLabel
20646                 }
20647             ]
20648         };
20649         
20650         return cfg;
20651     },
20652     
20653     initEvents : function() 
20654     {
20655         this.parent().register(this);
20656         
20657         this.el.on('click', this.onClick, this);
20658         
20659     },
20660     
20661     onClick : function()
20662     {
20663         this.setChecked(true);
20664     },
20665     
20666     setChecked : function(state, suppressEvent)
20667     {
20668         this.parent().setValue(this.value, suppressEvent);
20669         
20670     }
20671     
20672 });
20673  
20674
20675  //<script type="text/javascript">
20676
20677 /*
20678  * Based  Ext JS Library 1.1.1
20679  * Copyright(c) 2006-2007, Ext JS, LLC.
20680  * LGPL
20681  *
20682  */
20683  
20684 /**
20685  * @class Roo.HtmlEditorCore
20686  * @extends Roo.Component
20687  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20688  *
20689  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20690  */
20691
20692 Roo.HtmlEditorCore = function(config){
20693     
20694     
20695     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20696     
20697     
20698     this.addEvents({
20699         /**
20700          * @event initialize
20701          * Fires when the editor is fully initialized (including the iframe)
20702          * @param {Roo.HtmlEditorCore} this
20703          */
20704         initialize: true,
20705         /**
20706          * @event activate
20707          * Fires when the editor is first receives the focus. Any insertion must wait
20708          * until after this event.
20709          * @param {Roo.HtmlEditorCore} this
20710          */
20711         activate: true,
20712          /**
20713          * @event beforesync
20714          * Fires before the textarea is updated with content from the editor iframe. Return false
20715          * to cancel the sync.
20716          * @param {Roo.HtmlEditorCore} this
20717          * @param {String} html
20718          */
20719         beforesync: true,
20720          /**
20721          * @event beforepush
20722          * Fires before the iframe editor is updated with content from the textarea. Return false
20723          * to cancel the push.
20724          * @param {Roo.HtmlEditorCore} this
20725          * @param {String} html
20726          */
20727         beforepush: true,
20728          /**
20729          * @event sync
20730          * Fires when the textarea is updated with content from the editor iframe.
20731          * @param {Roo.HtmlEditorCore} this
20732          * @param {String} html
20733          */
20734         sync: true,
20735          /**
20736          * @event push
20737          * Fires when the iframe editor is updated with content from the textarea.
20738          * @param {Roo.HtmlEditorCore} this
20739          * @param {String} html
20740          */
20741         push: true,
20742         
20743         /**
20744          * @event editorevent
20745          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20746          * @param {Roo.HtmlEditorCore} this
20747          */
20748         editorevent: true
20749         
20750     });
20751     
20752     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20753     
20754     // defaults : white / black...
20755     this.applyBlacklists();
20756     
20757     
20758     
20759 };
20760
20761
20762 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20763
20764
20765      /**
20766      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20767      */
20768     
20769     owner : false,
20770     
20771      /**
20772      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20773      *                        Roo.resizable.
20774      */
20775     resizable : false,
20776      /**
20777      * @cfg {Number} height (in pixels)
20778      */   
20779     height: 300,
20780    /**
20781      * @cfg {Number} width (in pixels)
20782      */   
20783     width: 500,
20784     
20785     /**
20786      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20787      * 
20788      */
20789     stylesheets: false,
20790     
20791     // id of frame..
20792     frameId: false,
20793     
20794     // private properties
20795     validationEvent : false,
20796     deferHeight: true,
20797     initialized : false,
20798     activated : false,
20799     sourceEditMode : false,
20800     onFocus : Roo.emptyFn,
20801     iframePad:3,
20802     hideMode:'offsets',
20803     
20804     clearUp: true,
20805     
20806     // blacklist + whitelisted elements..
20807     black: false,
20808     white: false,
20809      
20810     
20811
20812     /**
20813      * Protected method that will not generally be called directly. It
20814      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20815      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20816      */
20817     getDocMarkup : function(){
20818         // body styles..
20819         var st = '';
20820         
20821         // inherit styels from page...?? 
20822         if (this.stylesheets === false) {
20823             
20824             Roo.get(document.head).select('style').each(function(node) {
20825                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20826             });
20827             
20828             Roo.get(document.head).select('link').each(function(node) { 
20829                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20830             });
20831             
20832         } else if (!this.stylesheets.length) {
20833                 // simple..
20834                 st = '<style type="text/css">' +
20835                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20836                    '</style>';
20837         } else { 
20838             
20839         }
20840         
20841         st +=  '<style type="text/css">' +
20842             'IMG { cursor: pointer } ' +
20843         '</style>';
20844
20845         
20846         return '<html><head>' + st  +
20847             //<style type="text/css">' +
20848             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20849             //'</style>' +
20850             ' </head><body class="roo-htmleditor-body"></body></html>';
20851     },
20852
20853     // private
20854     onRender : function(ct, position)
20855     {
20856         var _t = this;
20857         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20858         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20859         
20860         
20861         this.el.dom.style.border = '0 none';
20862         this.el.dom.setAttribute('tabIndex', -1);
20863         this.el.addClass('x-hidden hide');
20864         
20865         
20866         
20867         if(Roo.isIE){ // fix IE 1px bogus margin
20868             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20869         }
20870        
20871         
20872         this.frameId = Roo.id();
20873         
20874          
20875         
20876         var iframe = this.owner.wrap.createChild({
20877             tag: 'iframe',
20878             cls: 'form-control', // bootstrap..
20879             id: this.frameId,
20880             name: this.frameId,
20881             frameBorder : 'no',
20882             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20883         }, this.el
20884         );
20885         
20886         
20887         this.iframe = iframe.dom;
20888
20889          this.assignDocWin();
20890         
20891         this.doc.designMode = 'on';
20892        
20893         this.doc.open();
20894         this.doc.write(this.getDocMarkup());
20895         this.doc.close();
20896
20897         
20898         var task = { // must defer to wait for browser to be ready
20899             run : function(){
20900                 //console.log("run task?" + this.doc.readyState);
20901                 this.assignDocWin();
20902                 if(this.doc.body || this.doc.readyState == 'complete'){
20903                     try {
20904                         this.doc.designMode="on";
20905                     } catch (e) {
20906                         return;
20907                     }
20908                     Roo.TaskMgr.stop(task);
20909                     this.initEditor.defer(10, this);
20910                 }
20911             },
20912             interval : 10,
20913             duration: 10000,
20914             scope: this
20915         };
20916         Roo.TaskMgr.start(task);
20917
20918     },
20919
20920     // private
20921     onResize : function(w, h)
20922     {
20923          Roo.log('resize: ' +w + ',' + h );
20924         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20925         if(!this.iframe){
20926             return;
20927         }
20928         if(typeof w == 'number'){
20929             
20930             this.iframe.style.width = w + 'px';
20931         }
20932         if(typeof h == 'number'){
20933             
20934             this.iframe.style.height = h + 'px';
20935             if(this.doc){
20936                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20937             }
20938         }
20939         
20940     },
20941
20942     /**
20943      * Toggles the editor between standard and source edit mode.
20944      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20945      */
20946     toggleSourceEdit : function(sourceEditMode){
20947         
20948         this.sourceEditMode = sourceEditMode === true;
20949         
20950         if(this.sourceEditMode){
20951  
20952             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20953             
20954         }else{
20955             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20956             //this.iframe.className = '';
20957             this.deferFocus();
20958         }
20959         //this.setSize(this.owner.wrap.getSize());
20960         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20961     },
20962
20963     
20964   
20965
20966     /**
20967      * Protected method that will not generally be called directly. If you need/want
20968      * custom HTML cleanup, this is the method you should override.
20969      * @param {String} html The HTML to be cleaned
20970      * return {String} The cleaned HTML
20971      */
20972     cleanHtml : function(html){
20973         html = String(html);
20974         if(html.length > 5){
20975             if(Roo.isSafari){ // strip safari nonsense
20976                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20977             }
20978         }
20979         if(html == '&nbsp;'){
20980             html = '';
20981         }
20982         return html;
20983     },
20984
20985     /**
20986      * HTML Editor -> Textarea
20987      * Protected method that will not generally be called directly. Syncs the contents
20988      * of the editor iframe with the textarea.
20989      */
20990     syncValue : function(){
20991         if(this.initialized){
20992             var bd = (this.doc.body || this.doc.documentElement);
20993             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20994             var html = bd.innerHTML;
20995             if(Roo.isSafari){
20996                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20997                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20998                 if(m && m[1]){
20999                     html = '<div style="'+m[0]+'">' + html + '</div>';
21000                 }
21001             }
21002             html = this.cleanHtml(html);
21003             // fix up the special chars.. normaly like back quotes in word...
21004             // however we do not want to do this with chinese..
21005             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21006                 var cc = b.charCodeAt();
21007                 if (
21008                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21009                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21010                     (cc >= 0xf900 && cc < 0xfb00 )
21011                 ) {
21012                         return b;
21013                 }
21014                 return "&#"+cc+";" 
21015             });
21016             if(this.owner.fireEvent('beforesync', this, html) !== false){
21017                 this.el.dom.value = html;
21018                 this.owner.fireEvent('sync', this, html);
21019             }
21020         }
21021     },
21022
21023     /**
21024      * Protected method that will not generally be called directly. Pushes the value of the textarea
21025      * into the iframe editor.
21026      */
21027     pushValue : function(){
21028         if(this.initialized){
21029             var v = this.el.dom.value.trim();
21030             
21031 //            if(v.length < 1){
21032 //                v = '&#160;';
21033 //            }
21034             
21035             if(this.owner.fireEvent('beforepush', this, v) !== false){
21036                 var d = (this.doc.body || this.doc.documentElement);
21037                 d.innerHTML = v;
21038                 this.cleanUpPaste();
21039                 this.el.dom.value = d.innerHTML;
21040                 this.owner.fireEvent('push', this, v);
21041             }
21042         }
21043     },
21044
21045     // private
21046     deferFocus : function(){
21047         this.focus.defer(10, this);
21048     },
21049
21050     // doc'ed in Field
21051     focus : function(){
21052         if(this.win && !this.sourceEditMode){
21053             this.win.focus();
21054         }else{
21055             this.el.focus();
21056         }
21057     },
21058     
21059     assignDocWin: function()
21060     {
21061         var iframe = this.iframe;
21062         
21063          if(Roo.isIE){
21064             this.doc = iframe.contentWindow.document;
21065             this.win = iframe.contentWindow;
21066         } else {
21067 //            if (!Roo.get(this.frameId)) {
21068 //                return;
21069 //            }
21070 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21071 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21072             
21073             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21074                 return;
21075             }
21076             
21077             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21078             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21079         }
21080     },
21081     
21082     // private
21083     initEditor : function(){
21084         //console.log("INIT EDITOR");
21085         this.assignDocWin();
21086         
21087         
21088         
21089         this.doc.designMode="on";
21090         this.doc.open();
21091         this.doc.write(this.getDocMarkup());
21092         this.doc.close();
21093         
21094         var dbody = (this.doc.body || this.doc.documentElement);
21095         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21096         // this copies styles from the containing element into thsi one..
21097         // not sure why we need all of this..
21098         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21099         
21100         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21101         //ss['background-attachment'] = 'fixed'; // w3c
21102         dbody.bgProperties = 'fixed'; // ie
21103         //Roo.DomHelper.applyStyles(dbody, ss);
21104         Roo.EventManager.on(this.doc, {
21105             //'mousedown': this.onEditorEvent,
21106             'mouseup': this.onEditorEvent,
21107             'dblclick': this.onEditorEvent,
21108             'click': this.onEditorEvent,
21109             'keyup': this.onEditorEvent,
21110             buffer:100,
21111             scope: this
21112         });
21113         if(Roo.isGecko){
21114             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21115         }
21116         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21117             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21118         }
21119         this.initialized = true;
21120
21121         this.owner.fireEvent('initialize', this);
21122         this.pushValue();
21123     },
21124
21125     // private
21126     onDestroy : function(){
21127         
21128         
21129         
21130         if(this.rendered){
21131             
21132             //for (var i =0; i < this.toolbars.length;i++) {
21133             //    // fixme - ask toolbars for heights?
21134             //    this.toolbars[i].onDestroy();
21135            // }
21136             
21137             //this.wrap.dom.innerHTML = '';
21138             //this.wrap.remove();
21139         }
21140     },
21141
21142     // private
21143     onFirstFocus : function(){
21144         
21145         this.assignDocWin();
21146         
21147         
21148         this.activated = true;
21149          
21150     
21151         if(Roo.isGecko){ // prevent silly gecko errors
21152             this.win.focus();
21153             var s = this.win.getSelection();
21154             if(!s.focusNode || s.focusNode.nodeType != 3){
21155                 var r = s.getRangeAt(0);
21156                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21157                 r.collapse(true);
21158                 this.deferFocus();
21159             }
21160             try{
21161                 this.execCmd('useCSS', true);
21162                 this.execCmd('styleWithCSS', false);
21163             }catch(e){}
21164         }
21165         this.owner.fireEvent('activate', this);
21166     },
21167
21168     // private
21169     adjustFont: function(btn){
21170         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21171         //if(Roo.isSafari){ // safari
21172         //    adjust *= 2;
21173        // }
21174         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21175         if(Roo.isSafari){ // safari
21176             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21177             v =  (v < 10) ? 10 : v;
21178             v =  (v > 48) ? 48 : v;
21179             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21180             
21181         }
21182         
21183         
21184         v = Math.max(1, v+adjust);
21185         
21186         this.execCmd('FontSize', v  );
21187     },
21188
21189     onEditorEvent : function(e)
21190     {
21191         this.owner.fireEvent('editorevent', this, e);
21192       //  this.updateToolbar();
21193         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21194     },
21195
21196     insertTag : function(tg)
21197     {
21198         // could be a bit smarter... -> wrap the current selected tRoo..
21199         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21200             
21201             range = this.createRange(this.getSelection());
21202             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21203             wrappingNode.appendChild(range.extractContents());
21204             range.insertNode(wrappingNode);
21205
21206             return;
21207             
21208             
21209             
21210         }
21211         this.execCmd("formatblock",   tg);
21212         
21213     },
21214     
21215     insertText : function(txt)
21216     {
21217         
21218         
21219         var range = this.createRange();
21220         range.deleteContents();
21221                //alert(Sender.getAttribute('label'));
21222                
21223         range.insertNode(this.doc.createTextNode(txt));
21224     } ,
21225     
21226      
21227
21228     /**
21229      * Executes a Midas editor command on the editor document and performs necessary focus and
21230      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21231      * @param {String} cmd The Midas command
21232      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21233      */
21234     relayCmd : function(cmd, value){
21235         this.win.focus();
21236         this.execCmd(cmd, value);
21237         this.owner.fireEvent('editorevent', this);
21238         //this.updateToolbar();
21239         this.owner.deferFocus();
21240     },
21241
21242     /**
21243      * Executes a Midas editor command directly on the editor document.
21244      * For visual commands, you should use {@link #relayCmd} instead.
21245      * <b>This should only be called after the editor is initialized.</b>
21246      * @param {String} cmd The Midas command
21247      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21248      */
21249     execCmd : function(cmd, value){
21250         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21251         this.syncValue();
21252     },
21253  
21254  
21255    
21256     /**
21257      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21258      * to insert tRoo.
21259      * @param {String} text | dom node.. 
21260      */
21261     insertAtCursor : function(text)
21262     {
21263         
21264         
21265         
21266         if(!this.activated){
21267             return;
21268         }
21269         /*
21270         if(Roo.isIE){
21271             this.win.focus();
21272             var r = this.doc.selection.createRange();
21273             if(r){
21274                 r.collapse(true);
21275                 r.pasteHTML(text);
21276                 this.syncValue();
21277                 this.deferFocus();
21278             
21279             }
21280             return;
21281         }
21282         */
21283         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21284             this.win.focus();
21285             
21286             
21287             // from jquery ui (MIT licenced)
21288             var range, node;
21289             var win = this.win;
21290             
21291             if (win.getSelection && win.getSelection().getRangeAt) {
21292                 range = win.getSelection().getRangeAt(0);
21293                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21294                 range.insertNode(node);
21295             } else if (win.document.selection && win.document.selection.createRange) {
21296                 // no firefox support
21297                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21298                 win.document.selection.createRange().pasteHTML(txt);
21299             } else {
21300                 // no firefox support
21301                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21302                 this.execCmd('InsertHTML', txt);
21303             } 
21304             
21305             this.syncValue();
21306             
21307             this.deferFocus();
21308         }
21309     },
21310  // private
21311     mozKeyPress : function(e){
21312         if(e.ctrlKey){
21313             var c = e.getCharCode(), cmd;
21314           
21315             if(c > 0){
21316                 c = String.fromCharCode(c).toLowerCase();
21317                 switch(c){
21318                     case 'b':
21319                         cmd = 'bold';
21320                         break;
21321                     case 'i':
21322                         cmd = 'italic';
21323                         break;
21324                     
21325                     case 'u':
21326                         cmd = 'underline';
21327                         break;
21328                     
21329                     case 'v':
21330                         this.cleanUpPaste.defer(100, this);
21331                         return;
21332                         
21333                 }
21334                 if(cmd){
21335                     this.win.focus();
21336                     this.execCmd(cmd);
21337                     this.deferFocus();
21338                     e.preventDefault();
21339                 }
21340                 
21341             }
21342         }
21343     },
21344
21345     // private
21346     fixKeys : function(){ // load time branching for fastest keydown performance
21347         if(Roo.isIE){
21348             return function(e){
21349                 var k = e.getKey(), r;
21350                 if(k == e.TAB){
21351                     e.stopEvent();
21352                     r = this.doc.selection.createRange();
21353                     if(r){
21354                         r.collapse(true);
21355                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21356                         this.deferFocus();
21357                     }
21358                     return;
21359                 }
21360                 
21361                 if(k == e.ENTER){
21362                     r = this.doc.selection.createRange();
21363                     if(r){
21364                         var target = r.parentElement();
21365                         if(!target || target.tagName.toLowerCase() != 'li'){
21366                             e.stopEvent();
21367                             r.pasteHTML('<br />');
21368                             r.collapse(false);
21369                             r.select();
21370                         }
21371                     }
21372                 }
21373                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21374                     this.cleanUpPaste.defer(100, this);
21375                     return;
21376                 }
21377                 
21378                 
21379             };
21380         }else if(Roo.isOpera){
21381             return function(e){
21382                 var k = e.getKey();
21383                 if(k == e.TAB){
21384                     e.stopEvent();
21385                     this.win.focus();
21386                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21387                     this.deferFocus();
21388                 }
21389                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21390                     this.cleanUpPaste.defer(100, this);
21391                     return;
21392                 }
21393                 
21394             };
21395         }else if(Roo.isSafari){
21396             return function(e){
21397                 var k = e.getKey();
21398                 
21399                 if(k == e.TAB){
21400                     e.stopEvent();
21401                     this.execCmd('InsertText','\t');
21402                     this.deferFocus();
21403                     return;
21404                 }
21405                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21406                     this.cleanUpPaste.defer(100, this);
21407                     return;
21408                 }
21409                 
21410              };
21411         }
21412     }(),
21413     
21414     getAllAncestors: function()
21415     {
21416         var p = this.getSelectedNode();
21417         var a = [];
21418         if (!p) {
21419             a.push(p); // push blank onto stack..
21420             p = this.getParentElement();
21421         }
21422         
21423         
21424         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21425             a.push(p);
21426             p = p.parentNode;
21427         }
21428         a.push(this.doc.body);
21429         return a;
21430     },
21431     lastSel : false,
21432     lastSelNode : false,
21433     
21434     
21435     getSelection : function() 
21436     {
21437         this.assignDocWin();
21438         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21439     },
21440     
21441     getSelectedNode: function() 
21442     {
21443         // this may only work on Gecko!!!
21444         
21445         // should we cache this!!!!
21446         
21447         
21448         
21449          
21450         var range = this.createRange(this.getSelection()).cloneRange();
21451         
21452         if (Roo.isIE) {
21453             var parent = range.parentElement();
21454             while (true) {
21455                 var testRange = range.duplicate();
21456                 testRange.moveToElementText(parent);
21457                 if (testRange.inRange(range)) {
21458                     break;
21459                 }
21460                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21461                     break;
21462                 }
21463                 parent = parent.parentElement;
21464             }
21465             return parent;
21466         }
21467         
21468         // is ancestor a text element.
21469         var ac =  range.commonAncestorContainer;
21470         if (ac.nodeType == 3) {
21471             ac = ac.parentNode;
21472         }
21473         
21474         var ar = ac.childNodes;
21475          
21476         var nodes = [];
21477         var other_nodes = [];
21478         var has_other_nodes = false;
21479         for (var i=0;i<ar.length;i++) {
21480             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21481                 continue;
21482             }
21483             // fullly contained node.
21484             
21485             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21486                 nodes.push(ar[i]);
21487                 continue;
21488             }
21489             
21490             // probably selected..
21491             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21492                 other_nodes.push(ar[i]);
21493                 continue;
21494             }
21495             // outer..
21496             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21497                 continue;
21498             }
21499             
21500             
21501             has_other_nodes = true;
21502         }
21503         if (!nodes.length && other_nodes.length) {
21504             nodes= other_nodes;
21505         }
21506         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21507             return false;
21508         }
21509         
21510         return nodes[0];
21511     },
21512     createRange: function(sel)
21513     {
21514         // this has strange effects when using with 
21515         // top toolbar - not sure if it's a great idea.
21516         //this.editor.contentWindow.focus();
21517         if (typeof sel != "undefined") {
21518             try {
21519                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21520             } catch(e) {
21521                 return this.doc.createRange();
21522             }
21523         } else {
21524             return this.doc.createRange();
21525         }
21526     },
21527     getParentElement: function()
21528     {
21529         
21530         this.assignDocWin();
21531         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21532         
21533         var range = this.createRange(sel);
21534          
21535         try {
21536             var p = range.commonAncestorContainer;
21537             while (p.nodeType == 3) { // text node
21538                 p = p.parentNode;
21539             }
21540             return p;
21541         } catch (e) {
21542             return null;
21543         }
21544     
21545     },
21546     /***
21547      *
21548      * Range intersection.. the hard stuff...
21549      *  '-1' = before
21550      *  '0' = hits..
21551      *  '1' = after.
21552      *         [ -- selected range --- ]
21553      *   [fail]                        [fail]
21554      *
21555      *    basically..
21556      *      if end is before start or  hits it. fail.
21557      *      if start is after end or hits it fail.
21558      *
21559      *   if either hits (but other is outside. - then it's not 
21560      *   
21561      *    
21562      **/
21563     
21564     
21565     // @see http://www.thismuchiknow.co.uk/?p=64.
21566     rangeIntersectsNode : function(range, node)
21567     {
21568         var nodeRange = node.ownerDocument.createRange();
21569         try {
21570             nodeRange.selectNode(node);
21571         } catch (e) {
21572             nodeRange.selectNodeContents(node);
21573         }
21574     
21575         var rangeStartRange = range.cloneRange();
21576         rangeStartRange.collapse(true);
21577     
21578         var rangeEndRange = range.cloneRange();
21579         rangeEndRange.collapse(false);
21580     
21581         var nodeStartRange = nodeRange.cloneRange();
21582         nodeStartRange.collapse(true);
21583     
21584         var nodeEndRange = nodeRange.cloneRange();
21585         nodeEndRange.collapse(false);
21586     
21587         return rangeStartRange.compareBoundaryPoints(
21588                  Range.START_TO_START, nodeEndRange) == -1 &&
21589                rangeEndRange.compareBoundaryPoints(
21590                  Range.START_TO_START, nodeStartRange) == 1;
21591         
21592          
21593     },
21594     rangeCompareNode : function(range, node)
21595     {
21596         var nodeRange = node.ownerDocument.createRange();
21597         try {
21598             nodeRange.selectNode(node);
21599         } catch (e) {
21600             nodeRange.selectNodeContents(node);
21601         }
21602         
21603         
21604         range.collapse(true);
21605     
21606         nodeRange.collapse(true);
21607      
21608         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21609         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21610          
21611         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21612         
21613         var nodeIsBefore   =  ss == 1;
21614         var nodeIsAfter    = ee == -1;
21615         
21616         if (nodeIsBefore && nodeIsAfter) {
21617             return 0; // outer
21618         }
21619         if (!nodeIsBefore && nodeIsAfter) {
21620             return 1; //right trailed.
21621         }
21622         
21623         if (nodeIsBefore && !nodeIsAfter) {
21624             return 2;  // left trailed.
21625         }
21626         // fully contined.
21627         return 3;
21628     },
21629
21630     // private? - in a new class?
21631     cleanUpPaste :  function()
21632     {
21633         // cleans up the whole document..
21634         Roo.log('cleanuppaste');
21635         
21636         this.cleanUpChildren(this.doc.body);
21637         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21638         if (clean != this.doc.body.innerHTML) {
21639             this.doc.body.innerHTML = clean;
21640         }
21641         
21642     },
21643     
21644     cleanWordChars : function(input) {// change the chars to hex code
21645         var he = Roo.HtmlEditorCore;
21646         
21647         var output = input;
21648         Roo.each(he.swapCodes, function(sw) { 
21649             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21650             
21651             output = output.replace(swapper, sw[1]);
21652         });
21653         
21654         return output;
21655     },
21656     
21657     
21658     cleanUpChildren : function (n)
21659     {
21660         if (!n.childNodes.length) {
21661             return;
21662         }
21663         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21664            this.cleanUpChild(n.childNodes[i]);
21665         }
21666     },
21667     
21668     
21669         
21670     
21671     cleanUpChild : function (node)
21672     {
21673         var ed = this;
21674         //console.log(node);
21675         if (node.nodeName == "#text") {
21676             // clean up silly Windows -- stuff?
21677             return; 
21678         }
21679         if (node.nodeName == "#comment") {
21680             node.parentNode.removeChild(node);
21681             // clean up silly Windows -- stuff?
21682             return; 
21683         }
21684         var lcname = node.tagName.toLowerCase();
21685         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21686         // whitelist of tags..
21687         
21688         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21689             // remove node.
21690             node.parentNode.removeChild(node);
21691             return;
21692             
21693         }
21694         
21695         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21696         
21697         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21698         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21699         
21700         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21701         //    remove_keep_children = true;
21702         //}
21703         
21704         if (remove_keep_children) {
21705             this.cleanUpChildren(node);
21706             // inserts everything just before this node...
21707             while (node.childNodes.length) {
21708                 var cn = node.childNodes[0];
21709                 node.removeChild(cn);
21710                 node.parentNode.insertBefore(cn, node);
21711             }
21712             node.parentNode.removeChild(node);
21713             return;
21714         }
21715         
21716         if (!node.attributes || !node.attributes.length) {
21717             this.cleanUpChildren(node);
21718             return;
21719         }
21720         
21721         function cleanAttr(n,v)
21722         {
21723             
21724             if (v.match(/^\./) || v.match(/^\//)) {
21725                 return;
21726             }
21727             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21728                 return;
21729             }
21730             if (v.match(/^#/)) {
21731                 return;
21732             }
21733 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21734             node.removeAttribute(n);
21735             
21736         }
21737         
21738         var cwhite = this.cwhite;
21739         var cblack = this.cblack;
21740             
21741         function cleanStyle(n,v)
21742         {
21743             if (v.match(/expression/)) { //XSS?? should we even bother..
21744                 node.removeAttribute(n);
21745                 return;
21746             }
21747             
21748             var parts = v.split(/;/);
21749             var clean = [];
21750             
21751             Roo.each(parts, function(p) {
21752                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21753                 if (!p.length) {
21754                     return true;
21755                 }
21756                 var l = p.split(':').shift().replace(/\s+/g,'');
21757                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21758                 
21759                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21760 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21761                     //node.removeAttribute(n);
21762                     return true;
21763                 }
21764                 //Roo.log()
21765                 // only allow 'c whitelisted system attributes'
21766                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21767 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21768                     //node.removeAttribute(n);
21769                     return true;
21770                 }
21771                 
21772                 
21773                  
21774                 
21775                 clean.push(p);
21776                 return true;
21777             });
21778             if (clean.length) { 
21779                 node.setAttribute(n, clean.join(';'));
21780             } else {
21781                 node.removeAttribute(n);
21782             }
21783             
21784         }
21785         
21786         
21787         for (var i = node.attributes.length-1; i > -1 ; i--) {
21788             var a = node.attributes[i];
21789             //console.log(a);
21790             
21791             if (a.name.toLowerCase().substr(0,2)=='on')  {
21792                 node.removeAttribute(a.name);
21793                 continue;
21794             }
21795             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21796                 node.removeAttribute(a.name);
21797                 continue;
21798             }
21799             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21800                 cleanAttr(a.name,a.value); // fixme..
21801                 continue;
21802             }
21803             if (a.name == 'style') {
21804                 cleanStyle(a.name,a.value);
21805                 continue;
21806             }
21807             /// clean up MS crap..
21808             // tecnically this should be a list of valid class'es..
21809             
21810             
21811             if (a.name == 'class') {
21812                 if (a.value.match(/^Mso/)) {
21813                     node.className = '';
21814                 }
21815                 
21816                 if (a.value.match(/body/)) {
21817                     node.className = '';
21818                 }
21819                 continue;
21820             }
21821             
21822             // style cleanup!?
21823             // class cleanup?
21824             
21825         }
21826         
21827         
21828         this.cleanUpChildren(node);
21829         
21830         
21831     },
21832     
21833     /**
21834      * Clean up MS wordisms...
21835      */
21836     cleanWord : function(node)
21837     {
21838         
21839         
21840         if (!node) {
21841             this.cleanWord(this.doc.body);
21842             return;
21843         }
21844         if (node.nodeName == "#text") {
21845             // clean up silly Windows -- stuff?
21846             return; 
21847         }
21848         if (node.nodeName == "#comment") {
21849             node.parentNode.removeChild(node);
21850             // clean up silly Windows -- stuff?
21851             return; 
21852         }
21853         
21854         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21855             node.parentNode.removeChild(node);
21856             return;
21857         }
21858         
21859         // remove - but keep children..
21860         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21861             while (node.childNodes.length) {
21862                 var cn = node.childNodes[0];
21863                 node.removeChild(cn);
21864                 node.parentNode.insertBefore(cn, node);
21865             }
21866             node.parentNode.removeChild(node);
21867             this.iterateChildren(node, this.cleanWord);
21868             return;
21869         }
21870         // clean styles
21871         if (node.className.length) {
21872             
21873             var cn = node.className.split(/\W+/);
21874             var cna = [];
21875             Roo.each(cn, function(cls) {
21876                 if (cls.match(/Mso[a-zA-Z]+/)) {
21877                     return;
21878                 }
21879                 cna.push(cls);
21880             });
21881             node.className = cna.length ? cna.join(' ') : '';
21882             if (!cna.length) {
21883                 node.removeAttribute("class");
21884             }
21885         }
21886         
21887         if (node.hasAttribute("lang")) {
21888             node.removeAttribute("lang");
21889         }
21890         
21891         if (node.hasAttribute("style")) {
21892             
21893             var styles = node.getAttribute("style").split(";");
21894             var nstyle = [];
21895             Roo.each(styles, function(s) {
21896                 if (!s.match(/:/)) {
21897                     return;
21898                 }
21899                 var kv = s.split(":");
21900                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21901                     return;
21902                 }
21903                 // what ever is left... we allow.
21904                 nstyle.push(s);
21905             });
21906             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21907             if (!nstyle.length) {
21908                 node.removeAttribute('style');
21909             }
21910         }
21911         this.iterateChildren(node, this.cleanWord);
21912         
21913         
21914         
21915     },
21916     /**
21917      * iterateChildren of a Node, calling fn each time, using this as the scole..
21918      * @param {DomNode} node node to iterate children of.
21919      * @param {Function} fn method of this class to call on each item.
21920      */
21921     iterateChildren : function(node, fn)
21922     {
21923         if (!node.childNodes.length) {
21924                 return;
21925         }
21926         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21927            fn.call(this, node.childNodes[i])
21928         }
21929     },
21930     
21931     
21932     /**
21933      * cleanTableWidths.
21934      *
21935      * Quite often pasting from word etc.. results in tables with column and widths.
21936      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21937      *
21938      */
21939     cleanTableWidths : function(node)
21940     {
21941          
21942          
21943         if (!node) {
21944             this.cleanTableWidths(this.doc.body);
21945             return;
21946         }
21947         
21948         // ignore list...
21949         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21950             return; 
21951         }
21952         Roo.log(node.tagName);
21953         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21954             this.iterateChildren(node, this.cleanTableWidths);
21955             return;
21956         }
21957         if (node.hasAttribute('width')) {
21958             node.removeAttribute('width');
21959         }
21960         
21961          
21962         if (node.hasAttribute("style")) {
21963             // pretty basic...
21964             
21965             var styles = node.getAttribute("style").split(";");
21966             var nstyle = [];
21967             Roo.each(styles, function(s) {
21968                 if (!s.match(/:/)) {
21969                     return;
21970                 }
21971                 var kv = s.split(":");
21972                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21973                     return;
21974                 }
21975                 // what ever is left... we allow.
21976                 nstyle.push(s);
21977             });
21978             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21979             if (!nstyle.length) {
21980                 node.removeAttribute('style');
21981             }
21982         }
21983         
21984         this.iterateChildren(node, this.cleanTableWidths);
21985         
21986         
21987     },
21988     
21989     
21990     
21991     
21992     domToHTML : function(currentElement, depth, nopadtext) {
21993         
21994         depth = depth || 0;
21995         nopadtext = nopadtext || false;
21996     
21997         if (!currentElement) {
21998             return this.domToHTML(this.doc.body);
21999         }
22000         
22001         //Roo.log(currentElement);
22002         var j;
22003         var allText = false;
22004         var nodeName = currentElement.nodeName;
22005         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22006         
22007         if  (nodeName == '#text') {
22008             
22009             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22010         }
22011         
22012         
22013         var ret = '';
22014         if (nodeName != 'BODY') {
22015              
22016             var i = 0;
22017             // Prints the node tagName, such as <A>, <IMG>, etc
22018             if (tagName) {
22019                 var attr = [];
22020                 for(i = 0; i < currentElement.attributes.length;i++) {
22021                     // quoting?
22022                     var aname = currentElement.attributes.item(i).name;
22023                     if (!currentElement.attributes.item(i).value.length) {
22024                         continue;
22025                     }
22026                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22027                 }
22028                 
22029                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22030             } 
22031             else {
22032                 
22033                 // eack
22034             }
22035         } else {
22036             tagName = false;
22037         }
22038         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22039             return ret;
22040         }
22041         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22042             nopadtext = true;
22043         }
22044         
22045         
22046         // Traverse the tree
22047         i = 0;
22048         var currentElementChild = currentElement.childNodes.item(i);
22049         var allText = true;
22050         var innerHTML  = '';
22051         lastnode = '';
22052         while (currentElementChild) {
22053             // Formatting code (indent the tree so it looks nice on the screen)
22054             var nopad = nopadtext;
22055             if (lastnode == 'SPAN') {
22056                 nopad  = true;
22057             }
22058             // text
22059             if  (currentElementChild.nodeName == '#text') {
22060                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22061                 toadd = nopadtext ? toadd : toadd.trim();
22062                 if (!nopad && toadd.length > 80) {
22063                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22064                 }
22065                 innerHTML  += toadd;
22066                 
22067                 i++;
22068                 currentElementChild = currentElement.childNodes.item(i);
22069                 lastNode = '';
22070                 continue;
22071             }
22072             allText = false;
22073             
22074             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22075                 
22076             // Recursively traverse the tree structure of the child node
22077             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22078             lastnode = currentElementChild.nodeName;
22079             i++;
22080             currentElementChild=currentElement.childNodes.item(i);
22081         }
22082         
22083         ret += innerHTML;
22084         
22085         if (!allText) {
22086                 // The remaining code is mostly for formatting the tree
22087             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22088         }
22089         
22090         
22091         if (tagName) {
22092             ret+= "</"+tagName+">";
22093         }
22094         return ret;
22095         
22096     },
22097         
22098     applyBlacklists : function()
22099     {
22100         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22101         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22102         
22103         this.white = [];
22104         this.black = [];
22105         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22106             if (b.indexOf(tag) > -1) {
22107                 return;
22108             }
22109             this.white.push(tag);
22110             
22111         }, this);
22112         
22113         Roo.each(w, function(tag) {
22114             if (b.indexOf(tag) > -1) {
22115                 return;
22116             }
22117             if (this.white.indexOf(tag) > -1) {
22118                 return;
22119             }
22120             this.white.push(tag);
22121             
22122         }, this);
22123         
22124         
22125         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22126             if (w.indexOf(tag) > -1) {
22127                 return;
22128             }
22129             this.black.push(tag);
22130             
22131         }, this);
22132         
22133         Roo.each(b, function(tag) {
22134             if (w.indexOf(tag) > -1) {
22135                 return;
22136             }
22137             if (this.black.indexOf(tag) > -1) {
22138                 return;
22139             }
22140             this.black.push(tag);
22141             
22142         }, this);
22143         
22144         
22145         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22146         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22147         
22148         this.cwhite = [];
22149         this.cblack = [];
22150         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22151             if (b.indexOf(tag) > -1) {
22152                 return;
22153             }
22154             this.cwhite.push(tag);
22155             
22156         }, this);
22157         
22158         Roo.each(w, function(tag) {
22159             if (b.indexOf(tag) > -1) {
22160                 return;
22161             }
22162             if (this.cwhite.indexOf(tag) > -1) {
22163                 return;
22164             }
22165             this.cwhite.push(tag);
22166             
22167         }, this);
22168         
22169         
22170         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22171             if (w.indexOf(tag) > -1) {
22172                 return;
22173             }
22174             this.cblack.push(tag);
22175             
22176         }, this);
22177         
22178         Roo.each(b, function(tag) {
22179             if (w.indexOf(tag) > -1) {
22180                 return;
22181             }
22182             if (this.cblack.indexOf(tag) > -1) {
22183                 return;
22184             }
22185             this.cblack.push(tag);
22186             
22187         }, this);
22188     },
22189     
22190     setStylesheets : function(stylesheets)
22191     {
22192         if(typeof(stylesheets) == 'string'){
22193             Roo.get(this.iframe.contentDocument.head).createChild({
22194                 tag : 'link',
22195                 rel : 'stylesheet',
22196                 type : 'text/css',
22197                 href : stylesheets
22198             });
22199             
22200             return;
22201         }
22202         var _this = this;
22203      
22204         Roo.each(stylesheets, function(s) {
22205             if(!s.length){
22206                 return;
22207             }
22208             
22209             Roo.get(_this.iframe.contentDocument.head).createChild({
22210                 tag : 'link',
22211                 rel : 'stylesheet',
22212                 type : 'text/css',
22213                 href : s
22214             });
22215         });
22216
22217         
22218     },
22219     
22220     removeStylesheets : function()
22221     {
22222         var _this = this;
22223         
22224         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22225             s.remove();
22226         });
22227     }
22228     
22229     // hide stuff that is not compatible
22230     /**
22231      * @event blur
22232      * @hide
22233      */
22234     /**
22235      * @event change
22236      * @hide
22237      */
22238     /**
22239      * @event focus
22240      * @hide
22241      */
22242     /**
22243      * @event specialkey
22244      * @hide
22245      */
22246     /**
22247      * @cfg {String} fieldClass @hide
22248      */
22249     /**
22250      * @cfg {String} focusClass @hide
22251      */
22252     /**
22253      * @cfg {String} autoCreate @hide
22254      */
22255     /**
22256      * @cfg {String} inputType @hide
22257      */
22258     /**
22259      * @cfg {String} invalidClass @hide
22260      */
22261     /**
22262      * @cfg {String} invalidText @hide
22263      */
22264     /**
22265      * @cfg {String} msgFx @hide
22266      */
22267     /**
22268      * @cfg {String} validateOnBlur @hide
22269      */
22270 });
22271
22272 Roo.HtmlEditorCore.white = [
22273         'area', 'br', 'img', 'input', 'hr', 'wbr',
22274         
22275        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22276        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22277        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22278        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22279        'table',   'ul',         'xmp', 
22280        
22281        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22282       'thead',   'tr', 
22283      
22284       'dir', 'menu', 'ol', 'ul', 'dl',
22285        
22286       'embed',  'object'
22287 ];
22288
22289
22290 Roo.HtmlEditorCore.black = [
22291     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22292         'applet', // 
22293         'base',   'basefont', 'bgsound', 'blink',  'body', 
22294         'frame',  'frameset', 'head',    'html',   'ilayer', 
22295         'iframe', 'layer',  'link',     'meta',    'object',   
22296         'script', 'style' ,'title',  'xml' // clean later..
22297 ];
22298 Roo.HtmlEditorCore.clean = [
22299     'script', 'style', 'title', 'xml'
22300 ];
22301 Roo.HtmlEditorCore.remove = [
22302     'font'
22303 ];
22304 // attributes..
22305
22306 Roo.HtmlEditorCore.ablack = [
22307     'on'
22308 ];
22309     
22310 Roo.HtmlEditorCore.aclean = [ 
22311     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22312 ];
22313
22314 // protocols..
22315 Roo.HtmlEditorCore.pwhite= [
22316         'http',  'https',  'mailto'
22317 ];
22318
22319 // white listed style attributes.
22320 Roo.HtmlEditorCore.cwhite= [
22321       //  'text-align', /// default is to allow most things..
22322       
22323          
22324 //        'font-size'//??
22325 ];
22326
22327 // black listed style attributes.
22328 Roo.HtmlEditorCore.cblack= [
22329       //  'font-size' -- this can be set by the project 
22330 ];
22331
22332
22333 Roo.HtmlEditorCore.swapCodes   =[ 
22334     [    8211, "--" ], 
22335     [    8212, "--" ], 
22336     [    8216,  "'" ],  
22337     [    8217, "'" ],  
22338     [    8220, '"' ],  
22339     [    8221, '"' ],  
22340     [    8226, "*" ],  
22341     [    8230, "..." ]
22342 ]; 
22343
22344     /*
22345  * - LGPL
22346  *
22347  * HtmlEditor
22348  * 
22349  */
22350
22351 /**
22352  * @class Roo.bootstrap.HtmlEditor
22353  * @extends Roo.bootstrap.TextArea
22354  * Bootstrap HtmlEditor class
22355
22356  * @constructor
22357  * Create a new HtmlEditor
22358  * @param {Object} config The config object
22359  */
22360
22361 Roo.bootstrap.HtmlEditor = function(config){
22362     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22363     if (!this.toolbars) {
22364         this.toolbars = [];
22365     }
22366     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22367     this.addEvents({
22368             /**
22369              * @event initialize
22370              * Fires when the editor is fully initialized (including the iframe)
22371              * @param {HtmlEditor} this
22372              */
22373             initialize: true,
22374             /**
22375              * @event activate
22376              * Fires when the editor is first receives the focus. Any insertion must wait
22377              * until after this event.
22378              * @param {HtmlEditor} this
22379              */
22380             activate: true,
22381              /**
22382              * @event beforesync
22383              * Fires before the textarea is updated with content from the editor iframe. Return false
22384              * to cancel the sync.
22385              * @param {HtmlEditor} this
22386              * @param {String} html
22387              */
22388             beforesync: true,
22389              /**
22390              * @event beforepush
22391              * Fires before the iframe editor is updated with content from the textarea. Return false
22392              * to cancel the push.
22393              * @param {HtmlEditor} this
22394              * @param {String} html
22395              */
22396             beforepush: true,
22397              /**
22398              * @event sync
22399              * Fires when the textarea is updated with content from the editor iframe.
22400              * @param {HtmlEditor} this
22401              * @param {String} html
22402              */
22403             sync: true,
22404              /**
22405              * @event push
22406              * Fires when the iframe editor is updated with content from the textarea.
22407              * @param {HtmlEditor} this
22408              * @param {String} html
22409              */
22410             push: true,
22411              /**
22412              * @event editmodechange
22413              * Fires when the editor switches edit modes
22414              * @param {HtmlEditor} this
22415              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22416              */
22417             editmodechange: true,
22418             /**
22419              * @event editorevent
22420              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22421              * @param {HtmlEditor} this
22422              */
22423             editorevent: true,
22424             /**
22425              * @event firstfocus
22426              * Fires when on first focus - needed by toolbars..
22427              * @param {HtmlEditor} this
22428              */
22429             firstfocus: true,
22430             /**
22431              * @event autosave
22432              * Auto save the htmlEditor value as a file into Events
22433              * @param {HtmlEditor} this
22434              */
22435             autosave: true,
22436             /**
22437              * @event savedpreview
22438              * preview the saved version of htmlEditor
22439              * @param {HtmlEditor} this
22440              */
22441             savedpreview: true
22442         });
22443 };
22444
22445
22446 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22447     
22448     
22449       /**
22450      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22451      */
22452     toolbars : false,
22453    
22454      /**
22455      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22456      *                        Roo.resizable.
22457      */
22458     resizable : false,
22459      /**
22460      * @cfg {Number} height (in pixels)
22461      */   
22462     height: 300,
22463    /**
22464      * @cfg {Number} width (in pixels)
22465      */   
22466     width: false,
22467     
22468     /**
22469      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22470      * 
22471      */
22472     stylesheets: false,
22473     
22474     // id of frame..
22475     frameId: false,
22476     
22477     // private properties
22478     validationEvent : false,
22479     deferHeight: true,
22480     initialized : false,
22481     activated : false,
22482     
22483     onFocus : Roo.emptyFn,
22484     iframePad:3,
22485     hideMode:'offsets',
22486     
22487     
22488     tbContainer : false,
22489     
22490     toolbarContainer :function() {
22491         return this.wrap.select('.x-html-editor-tb',true).first();
22492     },
22493
22494     /**
22495      * Protected method that will not generally be called directly. It
22496      * is called when the editor creates its toolbar. Override this method if you need to
22497      * add custom toolbar buttons.
22498      * @param {HtmlEditor} editor
22499      */
22500     createToolbar : function(){
22501         
22502         Roo.log("create toolbars");
22503         
22504         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22505         this.toolbars[0].render(this.toolbarContainer());
22506         
22507         return;
22508         
22509 //        if (!editor.toolbars || !editor.toolbars.length) {
22510 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22511 //        }
22512 //        
22513 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22514 //            editor.toolbars[i] = Roo.factory(
22515 //                    typeof(editor.toolbars[i]) == 'string' ?
22516 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22517 //                Roo.bootstrap.HtmlEditor);
22518 //            editor.toolbars[i].init(editor);
22519 //        }
22520     },
22521
22522      
22523     // private
22524     onRender : function(ct, position)
22525     {
22526        // Roo.log("Call onRender: " + this.xtype);
22527         var _t = this;
22528         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22529       
22530         this.wrap = this.inputEl().wrap({
22531             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22532         });
22533         
22534         this.editorcore.onRender(ct, position);
22535          
22536         if (this.resizable) {
22537             this.resizeEl = new Roo.Resizable(this.wrap, {
22538                 pinned : true,
22539                 wrap: true,
22540                 dynamic : true,
22541                 minHeight : this.height,
22542                 height: this.height,
22543                 handles : this.resizable,
22544                 width: this.width,
22545                 listeners : {
22546                     resize : function(r, w, h) {
22547                         _t.onResize(w,h); // -something
22548                     }
22549                 }
22550             });
22551             
22552         }
22553         this.createToolbar(this);
22554        
22555         
22556         if(!this.width && this.resizable){
22557             this.setSize(this.wrap.getSize());
22558         }
22559         if (this.resizeEl) {
22560             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22561             // should trigger onReize..
22562         }
22563         
22564     },
22565
22566     // private
22567     onResize : function(w, h)
22568     {
22569         Roo.log('resize: ' +w + ',' + h );
22570         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22571         var ew = false;
22572         var eh = false;
22573         
22574         if(this.inputEl() ){
22575             if(typeof w == 'number'){
22576                 var aw = w - this.wrap.getFrameWidth('lr');
22577                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22578                 ew = aw;
22579             }
22580             if(typeof h == 'number'){
22581                  var tbh = -11;  // fixme it needs to tool bar size!
22582                 for (var i =0; i < this.toolbars.length;i++) {
22583                     // fixme - ask toolbars for heights?
22584                     tbh += this.toolbars[i].el.getHeight();
22585                     //if (this.toolbars[i].footer) {
22586                     //    tbh += this.toolbars[i].footer.el.getHeight();
22587                     //}
22588                 }
22589               
22590                 
22591                 
22592                 
22593                 
22594                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22595                 ah -= 5; // knock a few pixes off for look..
22596                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22597                 var eh = ah;
22598             }
22599         }
22600         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22601         this.editorcore.onResize(ew,eh);
22602         
22603     },
22604
22605     /**
22606      * Toggles the editor between standard and source edit mode.
22607      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22608      */
22609     toggleSourceEdit : function(sourceEditMode)
22610     {
22611         this.editorcore.toggleSourceEdit(sourceEditMode);
22612         
22613         if(this.editorcore.sourceEditMode){
22614             Roo.log('editor - showing textarea');
22615             
22616 //            Roo.log('in');
22617 //            Roo.log(this.syncValue());
22618             this.syncValue();
22619             this.inputEl().removeClass(['hide', 'x-hidden']);
22620             this.inputEl().dom.removeAttribute('tabIndex');
22621             this.inputEl().focus();
22622         }else{
22623             Roo.log('editor - hiding textarea');
22624 //            Roo.log('out')
22625 //            Roo.log(this.pushValue()); 
22626             this.pushValue();
22627             
22628             this.inputEl().addClass(['hide', 'x-hidden']);
22629             this.inputEl().dom.setAttribute('tabIndex', -1);
22630             //this.deferFocus();
22631         }
22632          
22633         if(this.resizable){
22634             this.setSize(this.wrap.getSize());
22635         }
22636         
22637         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22638     },
22639  
22640     // private (for BoxComponent)
22641     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22642
22643     // private (for BoxComponent)
22644     getResizeEl : function(){
22645         return this.wrap;
22646     },
22647
22648     // private (for BoxComponent)
22649     getPositionEl : function(){
22650         return this.wrap;
22651     },
22652
22653     // private
22654     initEvents : function(){
22655         this.originalValue = this.getValue();
22656     },
22657
22658 //    /**
22659 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22660 //     * @method
22661 //     */
22662 //    markInvalid : Roo.emptyFn,
22663 //    /**
22664 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22665 //     * @method
22666 //     */
22667 //    clearInvalid : Roo.emptyFn,
22668
22669     setValue : function(v){
22670         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22671         this.editorcore.pushValue();
22672     },
22673
22674      
22675     // private
22676     deferFocus : function(){
22677         this.focus.defer(10, this);
22678     },
22679
22680     // doc'ed in Field
22681     focus : function(){
22682         this.editorcore.focus();
22683         
22684     },
22685       
22686
22687     // private
22688     onDestroy : function(){
22689         
22690         
22691         
22692         if(this.rendered){
22693             
22694             for (var i =0; i < this.toolbars.length;i++) {
22695                 // fixme - ask toolbars for heights?
22696                 this.toolbars[i].onDestroy();
22697             }
22698             
22699             this.wrap.dom.innerHTML = '';
22700             this.wrap.remove();
22701         }
22702     },
22703
22704     // private
22705     onFirstFocus : function(){
22706         //Roo.log("onFirstFocus");
22707         this.editorcore.onFirstFocus();
22708          for (var i =0; i < this.toolbars.length;i++) {
22709             this.toolbars[i].onFirstFocus();
22710         }
22711         
22712     },
22713     
22714     // private
22715     syncValue : function()
22716     {   
22717         this.editorcore.syncValue();
22718     },
22719     
22720     pushValue : function()
22721     {   
22722         this.editorcore.pushValue();
22723     }
22724      
22725     
22726     // hide stuff that is not compatible
22727     /**
22728      * @event blur
22729      * @hide
22730      */
22731     /**
22732      * @event change
22733      * @hide
22734      */
22735     /**
22736      * @event focus
22737      * @hide
22738      */
22739     /**
22740      * @event specialkey
22741      * @hide
22742      */
22743     /**
22744      * @cfg {String} fieldClass @hide
22745      */
22746     /**
22747      * @cfg {String} focusClass @hide
22748      */
22749     /**
22750      * @cfg {String} autoCreate @hide
22751      */
22752     /**
22753      * @cfg {String} inputType @hide
22754      */
22755     /**
22756      * @cfg {String} invalidClass @hide
22757      */
22758     /**
22759      * @cfg {String} invalidText @hide
22760      */
22761     /**
22762      * @cfg {String} msgFx @hide
22763      */
22764     /**
22765      * @cfg {String} validateOnBlur @hide
22766      */
22767 });
22768  
22769     
22770    
22771    
22772    
22773       
22774 Roo.namespace('Roo.bootstrap.htmleditor');
22775 /**
22776  * @class Roo.bootstrap.HtmlEditorToolbar1
22777  * Basic Toolbar
22778  * 
22779  * Usage:
22780  *
22781  new Roo.bootstrap.HtmlEditor({
22782     ....
22783     toolbars : [
22784         new Roo.bootstrap.HtmlEditorToolbar1({
22785             disable : { fonts: 1 , format: 1, ..., ... , ...],
22786             btns : [ .... ]
22787         })
22788     }
22789      
22790  * 
22791  * @cfg {Object} disable List of elements to disable..
22792  * @cfg {Array} btns List of additional buttons.
22793  * 
22794  * 
22795  * NEEDS Extra CSS? 
22796  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22797  */
22798  
22799 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22800 {
22801     
22802     Roo.apply(this, config);
22803     
22804     // default disabled, based on 'good practice'..
22805     this.disable = this.disable || {};
22806     Roo.applyIf(this.disable, {
22807         fontSize : true,
22808         colors : true,
22809         specialElements : true
22810     });
22811     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22812     
22813     this.editor = config.editor;
22814     this.editorcore = config.editor.editorcore;
22815     
22816     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22817     
22818     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22819     // dont call parent... till later.
22820 }
22821 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22822      
22823     bar : true,
22824     
22825     editor : false,
22826     editorcore : false,
22827     
22828     
22829     formats : [
22830         "p" ,  
22831         "h1","h2","h3","h4","h5","h6", 
22832         "pre", "code", 
22833         "abbr", "acronym", "address", "cite", "samp", "var",
22834         'div','span'
22835     ],
22836     
22837     onRender : function(ct, position)
22838     {
22839        // Roo.log("Call onRender: " + this.xtype);
22840         
22841        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22842        Roo.log(this.el);
22843        this.el.dom.style.marginBottom = '0';
22844        var _this = this;
22845        var editorcore = this.editorcore;
22846        var editor= this.editor;
22847        
22848        var children = [];
22849        var btn = function(id,cmd , toggle, handler){
22850        
22851             var  event = toggle ? 'toggle' : 'click';
22852        
22853             var a = {
22854                 size : 'sm',
22855                 xtype: 'Button',
22856                 xns: Roo.bootstrap,
22857                 glyphicon : id,
22858                 cmd : id || cmd,
22859                 enableToggle:toggle !== false,
22860                 //html : 'submit'
22861                 pressed : toggle ? false : null,
22862                 listeners : {}
22863             };
22864             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22865                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22866             };
22867             children.push(a);
22868             return a;
22869        }
22870         
22871         var style = {
22872                 xtype: 'Button',
22873                 size : 'sm',
22874                 xns: Roo.bootstrap,
22875                 glyphicon : 'font',
22876                 //html : 'submit'
22877                 menu : {
22878                     xtype: 'Menu',
22879                     xns: Roo.bootstrap,
22880                     items:  []
22881                 }
22882         };
22883         Roo.each(this.formats, function(f) {
22884             style.menu.items.push({
22885                 xtype :'MenuItem',
22886                 xns: Roo.bootstrap,
22887                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22888                 tagname : f,
22889                 listeners : {
22890                     click : function()
22891                     {
22892                         editorcore.insertTag(this.tagname);
22893                         editor.focus();
22894                     }
22895                 }
22896                 
22897             });
22898         });
22899          children.push(style);   
22900             
22901             
22902         btn('bold',false,true);
22903         btn('italic',false,true);
22904         btn('align-left', 'justifyleft',true);
22905         btn('align-center', 'justifycenter',true);
22906         btn('align-right' , 'justifyright',true);
22907         btn('link', false, false, function(btn) {
22908             //Roo.log("create link?");
22909             var url = prompt(this.createLinkText, this.defaultLinkValue);
22910             if(url && url != 'http:/'+'/'){
22911                 this.editorcore.relayCmd('createlink', url);
22912             }
22913         }),
22914         btn('list','insertunorderedlist',true);
22915         btn('pencil', false,true, function(btn){
22916                 Roo.log(this);
22917                 
22918                 this.toggleSourceEdit(btn.pressed);
22919         });
22920         /*
22921         var cog = {
22922                 xtype: 'Button',
22923                 size : 'sm',
22924                 xns: Roo.bootstrap,
22925                 glyphicon : 'cog',
22926                 //html : 'submit'
22927                 menu : {
22928                     xtype: 'Menu',
22929                     xns: Roo.bootstrap,
22930                     items:  []
22931                 }
22932         };
22933         
22934         cog.menu.items.push({
22935             xtype :'MenuItem',
22936             xns: Roo.bootstrap,
22937             html : Clean styles,
22938             tagname : f,
22939             listeners : {
22940                 click : function()
22941                 {
22942                     editorcore.insertTag(this.tagname);
22943                     editor.focus();
22944                 }
22945             }
22946             
22947         });
22948        */
22949         
22950          
22951        this.xtype = 'NavSimplebar';
22952         
22953         for(var i=0;i< children.length;i++) {
22954             
22955             this.buttons.add(this.addxtypeChild(children[i]));
22956             
22957         }
22958         
22959         editor.on('editorevent', this.updateToolbar, this);
22960     },
22961     onBtnClick : function(id)
22962     {
22963        this.editorcore.relayCmd(id);
22964        this.editorcore.focus();
22965     },
22966     
22967     /**
22968      * Protected method that will not generally be called directly. It triggers
22969      * a toolbar update by reading the markup state of the current selection in the editor.
22970      */
22971     updateToolbar: function(){
22972
22973         if(!this.editorcore.activated){
22974             this.editor.onFirstFocus(); // is this neeed?
22975             return;
22976         }
22977
22978         var btns = this.buttons; 
22979         var doc = this.editorcore.doc;
22980         btns.get('bold').setActive(doc.queryCommandState('bold'));
22981         btns.get('italic').setActive(doc.queryCommandState('italic'));
22982         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22983         
22984         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22985         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22986         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22987         
22988         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22989         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22990          /*
22991         
22992         var ans = this.editorcore.getAllAncestors();
22993         if (this.formatCombo) {
22994             
22995             
22996             var store = this.formatCombo.store;
22997             this.formatCombo.setValue("");
22998             for (var i =0; i < ans.length;i++) {
22999                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23000                     // select it..
23001                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23002                     break;
23003                 }
23004             }
23005         }
23006         
23007         
23008         
23009         // hides menus... - so this cant be on a menu...
23010         Roo.bootstrap.MenuMgr.hideAll();
23011         */
23012         Roo.bootstrap.MenuMgr.hideAll();
23013         //this.editorsyncValue();
23014     },
23015     onFirstFocus: function() {
23016         this.buttons.each(function(item){
23017            item.enable();
23018         });
23019     },
23020     toggleSourceEdit : function(sourceEditMode){
23021         
23022           
23023         if(sourceEditMode){
23024             Roo.log("disabling buttons");
23025            this.buttons.each( function(item){
23026                 if(item.cmd != 'pencil'){
23027                     item.disable();
23028                 }
23029             });
23030           
23031         }else{
23032             Roo.log("enabling buttons");
23033             if(this.editorcore.initialized){
23034                 this.buttons.each( function(item){
23035                     item.enable();
23036                 });
23037             }
23038             
23039         }
23040         Roo.log("calling toggole on editor");
23041         // tell the editor that it's been pressed..
23042         this.editor.toggleSourceEdit(sourceEditMode);
23043        
23044     }
23045 });
23046
23047
23048
23049
23050
23051 /**
23052  * @class Roo.bootstrap.Table.AbstractSelectionModel
23053  * @extends Roo.util.Observable
23054  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23055  * implemented by descendant classes.  This class should not be directly instantiated.
23056  * @constructor
23057  */
23058 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23059     this.locked = false;
23060     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23061 };
23062
23063
23064 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23065     /** @ignore Called by the grid automatically. Do not call directly. */
23066     init : function(grid){
23067         this.grid = grid;
23068         this.initEvents();
23069     },
23070
23071     /**
23072      * Locks the selections.
23073      */
23074     lock : function(){
23075         this.locked = true;
23076     },
23077
23078     /**
23079      * Unlocks the selections.
23080      */
23081     unlock : function(){
23082         this.locked = false;
23083     },
23084
23085     /**
23086      * Returns true if the selections are locked.
23087      * @return {Boolean}
23088      */
23089     isLocked : function(){
23090         return this.locked;
23091     }
23092 });
23093 /**
23094  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23095  * @class Roo.bootstrap.Table.RowSelectionModel
23096  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23097  * It supports multiple selections and keyboard selection/navigation. 
23098  * @constructor
23099  * @param {Object} config
23100  */
23101
23102 Roo.bootstrap.Table.RowSelectionModel = function(config){
23103     Roo.apply(this, config);
23104     this.selections = new Roo.util.MixedCollection(false, function(o){
23105         return o.id;
23106     });
23107
23108     this.last = false;
23109     this.lastActive = false;
23110
23111     this.addEvents({
23112         /**
23113              * @event selectionchange
23114              * Fires when the selection changes
23115              * @param {SelectionModel} this
23116              */
23117             "selectionchange" : true,
23118         /**
23119              * @event afterselectionchange
23120              * Fires after the selection changes (eg. by key press or clicking)
23121              * @param {SelectionModel} this
23122              */
23123             "afterselectionchange" : true,
23124         /**
23125              * @event beforerowselect
23126              * Fires when a row is selected being selected, return false to cancel.
23127              * @param {SelectionModel} this
23128              * @param {Number} rowIndex The selected index
23129              * @param {Boolean} keepExisting False if other selections will be cleared
23130              */
23131             "beforerowselect" : true,
23132         /**
23133              * @event rowselect
23134              * Fires when a row is selected.
23135              * @param {SelectionModel} this
23136              * @param {Number} rowIndex The selected index
23137              * @param {Roo.data.Record} r The record
23138              */
23139             "rowselect" : true,
23140         /**
23141              * @event rowdeselect
23142              * Fires when a row is deselected.
23143              * @param {SelectionModel} this
23144              * @param {Number} rowIndex The selected index
23145              */
23146         "rowdeselect" : true
23147     });
23148     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23149     this.locked = false;
23150  };
23151
23152 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23153     /**
23154      * @cfg {Boolean} singleSelect
23155      * True to allow selection of only one row at a time (defaults to false)
23156      */
23157     singleSelect : false,
23158
23159     // private
23160     initEvents : function()
23161     {
23162
23163         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23164         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23165         //}else{ // allow click to work like normal
23166          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23167         //}
23168         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23169         this.grid.on("rowclick", this.handleMouseDown, this);
23170         
23171         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23172             "up" : function(e){
23173                 if(!e.shiftKey){
23174                     this.selectPrevious(e.shiftKey);
23175                 }else if(this.last !== false && this.lastActive !== false){
23176                     var last = this.last;
23177                     this.selectRange(this.last,  this.lastActive-1);
23178                     this.grid.getView().focusRow(this.lastActive);
23179                     if(last !== false){
23180                         this.last = last;
23181                     }
23182                 }else{
23183                     this.selectFirstRow();
23184                 }
23185                 this.fireEvent("afterselectionchange", this);
23186             },
23187             "down" : function(e){
23188                 if(!e.shiftKey){
23189                     this.selectNext(e.shiftKey);
23190                 }else if(this.last !== false && this.lastActive !== false){
23191                     var last = this.last;
23192                     this.selectRange(this.last,  this.lastActive+1);
23193                     this.grid.getView().focusRow(this.lastActive);
23194                     if(last !== false){
23195                         this.last = last;
23196                     }
23197                 }else{
23198                     this.selectFirstRow();
23199                 }
23200                 this.fireEvent("afterselectionchange", this);
23201             },
23202             scope: this
23203         });
23204         this.grid.store.on('load', function(){
23205             this.selections.clear();
23206         },this);
23207         /*
23208         var view = this.grid.view;
23209         view.on("refresh", this.onRefresh, this);
23210         view.on("rowupdated", this.onRowUpdated, this);
23211         view.on("rowremoved", this.onRemove, this);
23212         */
23213     },
23214
23215     // private
23216     onRefresh : function()
23217     {
23218         var ds = this.grid.store, i, v = this.grid.view;
23219         var s = this.selections;
23220         s.each(function(r){
23221             if((i = ds.indexOfId(r.id)) != -1){
23222                 v.onRowSelect(i);
23223             }else{
23224                 s.remove(r);
23225             }
23226         });
23227     },
23228
23229     // private
23230     onRemove : function(v, index, r){
23231         this.selections.remove(r);
23232     },
23233
23234     // private
23235     onRowUpdated : function(v, index, r){
23236         if(this.isSelected(r)){
23237             v.onRowSelect(index);
23238         }
23239     },
23240
23241     /**
23242      * Select records.
23243      * @param {Array} records The records to select
23244      * @param {Boolean} keepExisting (optional) True to keep existing selections
23245      */
23246     selectRecords : function(records, keepExisting)
23247     {
23248         if(!keepExisting){
23249             this.clearSelections();
23250         }
23251             var ds = this.grid.store;
23252         for(var i = 0, len = records.length; i < len; i++){
23253             this.selectRow(ds.indexOf(records[i]), true);
23254         }
23255     },
23256
23257     /**
23258      * Gets the number of selected rows.
23259      * @return {Number}
23260      */
23261     getCount : function(){
23262         return this.selections.length;
23263     },
23264
23265     /**
23266      * Selects the first row in the grid.
23267      */
23268     selectFirstRow : function(){
23269         this.selectRow(0);
23270     },
23271
23272     /**
23273      * Select the last row.
23274      * @param {Boolean} keepExisting (optional) True to keep existing selections
23275      */
23276     selectLastRow : function(keepExisting){
23277         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23278         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23279     },
23280
23281     /**
23282      * Selects the row immediately following the last selected row.
23283      * @param {Boolean} keepExisting (optional) True to keep existing selections
23284      */
23285     selectNext : function(keepExisting)
23286     {
23287             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23288             this.selectRow(this.last+1, keepExisting);
23289             this.grid.getView().focusRow(this.last);
23290         }
23291     },
23292
23293     /**
23294      * Selects the row that precedes the last selected row.
23295      * @param {Boolean} keepExisting (optional) True to keep existing selections
23296      */
23297     selectPrevious : function(keepExisting){
23298         if(this.last){
23299             this.selectRow(this.last-1, keepExisting);
23300             this.grid.getView().focusRow(this.last);
23301         }
23302     },
23303
23304     /**
23305      * Returns the selected records
23306      * @return {Array} Array of selected records
23307      */
23308     getSelections : function(){
23309         return [].concat(this.selections.items);
23310     },
23311
23312     /**
23313      * Returns the first selected record.
23314      * @return {Record}
23315      */
23316     getSelected : function(){
23317         return this.selections.itemAt(0);
23318     },
23319
23320
23321     /**
23322      * Clears all selections.
23323      */
23324     clearSelections : function(fast)
23325     {
23326         if(this.locked) {
23327             return;
23328         }
23329         if(fast !== true){
23330                 var ds = this.grid.store;
23331             var s = this.selections;
23332             s.each(function(r){
23333                 this.deselectRow(ds.indexOfId(r.id));
23334             }, this);
23335             s.clear();
23336         }else{
23337             this.selections.clear();
23338         }
23339         this.last = false;
23340     },
23341
23342
23343     /**
23344      * Selects all rows.
23345      */
23346     selectAll : function(){
23347         if(this.locked) {
23348             return;
23349         }
23350         this.selections.clear();
23351         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23352             this.selectRow(i, true);
23353         }
23354     },
23355
23356     /**
23357      * Returns True if there is a selection.
23358      * @return {Boolean}
23359      */
23360     hasSelection : function(){
23361         return this.selections.length > 0;
23362     },
23363
23364     /**
23365      * Returns True if the specified row is selected.
23366      * @param {Number/Record} record The record or index of the record to check
23367      * @return {Boolean}
23368      */
23369     isSelected : function(index){
23370             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23371         return (r && this.selections.key(r.id) ? true : false);
23372     },
23373
23374     /**
23375      * Returns True if the specified record id is selected.
23376      * @param {String} id The id of record to check
23377      * @return {Boolean}
23378      */
23379     isIdSelected : function(id){
23380         return (this.selections.key(id) ? true : false);
23381     },
23382
23383
23384     // private
23385     handleMouseDBClick : function(e, t){
23386         
23387     },
23388     // private
23389     handleMouseDown : function(e, t)
23390     {
23391             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23392         if(this.isLocked() || rowIndex < 0 ){
23393             return;
23394         };
23395         if(e.shiftKey && this.last !== false){
23396             var last = this.last;
23397             this.selectRange(last, rowIndex, e.ctrlKey);
23398             this.last = last; // reset the last
23399             t.focus();
23400     
23401         }else{
23402             var isSelected = this.isSelected(rowIndex);
23403             //Roo.log("select row:" + rowIndex);
23404             if(isSelected){
23405                 this.deselectRow(rowIndex);
23406             } else {
23407                         this.selectRow(rowIndex, true);
23408             }
23409     
23410             /*
23411                 if(e.button !== 0 && isSelected){
23412                 alert('rowIndex 2: ' + rowIndex);
23413                     view.focusRow(rowIndex);
23414                 }else if(e.ctrlKey && isSelected){
23415                     this.deselectRow(rowIndex);
23416                 }else if(!isSelected){
23417                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23418                     view.focusRow(rowIndex);
23419                 }
23420             */
23421         }
23422         this.fireEvent("afterselectionchange", this);
23423     },
23424     // private
23425     handleDragableRowClick :  function(grid, rowIndex, e) 
23426     {
23427         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23428             this.selectRow(rowIndex, false);
23429             grid.view.focusRow(rowIndex);
23430              this.fireEvent("afterselectionchange", this);
23431         }
23432     },
23433     
23434     /**
23435      * Selects multiple rows.
23436      * @param {Array} rows Array of the indexes of the row to select
23437      * @param {Boolean} keepExisting (optional) True to keep existing selections
23438      */
23439     selectRows : function(rows, keepExisting){
23440         if(!keepExisting){
23441             this.clearSelections();
23442         }
23443         for(var i = 0, len = rows.length; i < len; i++){
23444             this.selectRow(rows[i], true);
23445         }
23446     },
23447
23448     /**
23449      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23450      * @param {Number} startRow The index of the first row in the range
23451      * @param {Number} endRow The index of the last row in the range
23452      * @param {Boolean} keepExisting (optional) True to retain existing selections
23453      */
23454     selectRange : function(startRow, endRow, keepExisting){
23455         if(this.locked) {
23456             return;
23457         }
23458         if(!keepExisting){
23459             this.clearSelections();
23460         }
23461         if(startRow <= endRow){
23462             for(var i = startRow; i <= endRow; i++){
23463                 this.selectRow(i, true);
23464             }
23465         }else{
23466             for(var i = startRow; i >= endRow; i--){
23467                 this.selectRow(i, true);
23468             }
23469         }
23470     },
23471
23472     /**
23473      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23474      * @param {Number} startRow The index of the first row in the range
23475      * @param {Number} endRow The index of the last row in the range
23476      */
23477     deselectRange : function(startRow, endRow, preventViewNotify){
23478         if(this.locked) {
23479             return;
23480         }
23481         for(var i = startRow; i <= endRow; i++){
23482             this.deselectRow(i, preventViewNotify);
23483         }
23484     },
23485
23486     /**
23487      * Selects a row.
23488      * @param {Number} row The index of the row to select
23489      * @param {Boolean} keepExisting (optional) True to keep existing selections
23490      */
23491     selectRow : function(index, keepExisting, preventViewNotify)
23492     {
23493             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23494             return;
23495         }
23496         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23497             if(!keepExisting || this.singleSelect){
23498                 this.clearSelections();
23499             }
23500             
23501             var r = this.grid.store.getAt(index);
23502             //console.log('selectRow - record id :' + r.id);
23503             
23504             this.selections.add(r);
23505             this.last = this.lastActive = index;
23506             if(!preventViewNotify){
23507                 var proxy = new Roo.Element(
23508                                 this.grid.getRowDom(index)
23509                 );
23510                 proxy.addClass('bg-info info');
23511             }
23512             this.fireEvent("rowselect", this, index, r);
23513             this.fireEvent("selectionchange", this);
23514         }
23515     },
23516
23517     /**
23518      * Deselects a row.
23519      * @param {Number} row The index of the row to deselect
23520      */
23521     deselectRow : function(index, preventViewNotify)
23522     {
23523         if(this.locked) {
23524             return;
23525         }
23526         if(this.last == index){
23527             this.last = false;
23528         }
23529         if(this.lastActive == index){
23530             this.lastActive = false;
23531         }
23532         
23533         var r = this.grid.store.getAt(index);
23534         if (!r) {
23535             return;
23536         }
23537         
23538         this.selections.remove(r);
23539         //.console.log('deselectRow - record id :' + r.id);
23540         if(!preventViewNotify){
23541         
23542             var proxy = new Roo.Element(
23543                 this.grid.getRowDom(index)
23544             );
23545             proxy.removeClass('bg-info info');
23546         }
23547         this.fireEvent("rowdeselect", this, index);
23548         this.fireEvent("selectionchange", this);
23549     },
23550
23551     // private
23552     restoreLast : function(){
23553         if(this._last){
23554             this.last = this._last;
23555         }
23556     },
23557
23558     // private
23559     acceptsNav : function(row, col, cm){
23560         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23561     },
23562
23563     // private
23564     onEditorKey : function(field, e){
23565         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23566         if(k == e.TAB){
23567             e.stopEvent();
23568             ed.completeEdit();
23569             if(e.shiftKey){
23570                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23571             }else{
23572                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23573             }
23574         }else if(k == e.ENTER && !e.ctrlKey){
23575             e.stopEvent();
23576             ed.completeEdit();
23577             if(e.shiftKey){
23578                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23579             }else{
23580                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23581             }
23582         }else if(k == e.ESC){
23583             ed.cancelEdit();
23584         }
23585         if(newCell){
23586             g.startEditing(newCell[0], newCell[1]);
23587         }
23588     }
23589 });
23590 /*
23591  * Based on:
23592  * Ext JS Library 1.1.1
23593  * Copyright(c) 2006-2007, Ext JS, LLC.
23594  *
23595  * Originally Released Under LGPL - original licence link has changed is not relivant.
23596  *
23597  * Fork - LGPL
23598  * <script type="text/javascript">
23599  */
23600  
23601 /**
23602  * @class Roo.bootstrap.PagingToolbar
23603  * @extends Roo.bootstrap.NavSimplebar
23604  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23605  * @constructor
23606  * Create a new PagingToolbar
23607  * @param {Object} config The config object
23608  * @param {Roo.data.Store} store
23609  */
23610 Roo.bootstrap.PagingToolbar = function(config)
23611 {
23612     // old args format still supported... - xtype is prefered..
23613         // created from xtype...
23614     
23615     this.ds = config.dataSource;
23616     
23617     if (config.store && !this.ds) {
23618         this.store= Roo.factory(config.store, Roo.data);
23619         this.ds = this.store;
23620         this.ds.xmodule = this.xmodule || false;
23621     }
23622     
23623     this.toolbarItems = [];
23624     if (config.items) {
23625         this.toolbarItems = config.items;
23626     }
23627     
23628     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23629     
23630     this.cursor = 0;
23631     
23632     if (this.ds) { 
23633         this.bind(this.ds);
23634     }
23635     
23636     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23637     
23638 };
23639
23640 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23641     /**
23642      * @cfg {Roo.data.Store} dataSource
23643      * The underlying data store providing the paged data
23644      */
23645     /**
23646      * @cfg {String/HTMLElement/Element} container
23647      * container The id or element that will contain the toolbar
23648      */
23649     /**
23650      * @cfg {Boolean} displayInfo
23651      * True to display the displayMsg (defaults to false)
23652      */
23653     /**
23654      * @cfg {Number} pageSize
23655      * The number of records to display per page (defaults to 20)
23656      */
23657     pageSize: 20,
23658     /**
23659      * @cfg {String} displayMsg
23660      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23661      */
23662     displayMsg : 'Displaying {0} - {1} of {2}',
23663     /**
23664      * @cfg {String} emptyMsg
23665      * The message to display when no records are found (defaults to "No data to display")
23666      */
23667     emptyMsg : 'No data to display',
23668     /**
23669      * Customizable piece of the default paging text (defaults to "Page")
23670      * @type String
23671      */
23672     beforePageText : "Page",
23673     /**
23674      * Customizable piece of the default paging text (defaults to "of %0")
23675      * @type String
23676      */
23677     afterPageText : "of {0}",
23678     /**
23679      * Customizable piece of the default paging text (defaults to "First Page")
23680      * @type String
23681      */
23682     firstText : "First Page",
23683     /**
23684      * Customizable piece of the default paging text (defaults to "Previous Page")
23685      * @type String
23686      */
23687     prevText : "Previous Page",
23688     /**
23689      * Customizable piece of the default paging text (defaults to "Next Page")
23690      * @type String
23691      */
23692     nextText : "Next Page",
23693     /**
23694      * Customizable piece of the default paging text (defaults to "Last Page")
23695      * @type String
23696      */
23697     lastText : "Last Page",
23698     /**
23699      * Customizable piece of the default paging text (defaults to "Refresh")
23700      * @type String
23701      */
23702     refreshText : "Refresh",
23703
23704     buttons : false,
23705     // private
23706     onRender : function(ct, position) 
23707     {
23708         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23709         this.navgroup.parentId = this.id;
23710         this.navgroup.onRender(this.el, null);
23711         // add the buttons to the navgroup
23712         
23713         if(this.displayInfo){
23714             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23715             this.displayEl = this.el.select('.x-paging-info', true).first();
23716 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23717 //            this.displayEl = navel.el.select('span',true).first();
23718         }
23719         
23720         var _this = this;
23721         
23722         if(this.buttons){
23723             Roo.each(_this.buttons, function(e){ // this might need to use render????
23724                Roo.factory(e).onRender(_this.el, null);
23725             });
23726         }
23727             
23728         Roo.each(_this.toolbarItems, function(e) {
23729             _this.navgroup.addItem(e);
23730         });
23731         
23732         
23733         this.first = this.navgroup.addItem({
23734             tooltip: this.firstText,
23735             cls: "prev",
23736             icon : 'fa fa-backward',
23737             disabled: true,
23738             preventDefault: true,
23739             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23740         });
23741         
23742         this.prev =  this.navgroup.addItem({
23743             tooltip: this.prevText,
23744             cls: "prev",
23745             icon : 'fa fa-step-backward',
23746             disabled: true,
23747             preventDefault: true,
23748             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23749         });
23750     //this.addSeparator();
23751         
23752         
23753         var field = this.navgroup.addItem( {
23754             tagtype : 'span',
23755             cls : 'x-paging-position',
23756             
23757             html : this.beforePageText  +
23758                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23759                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23760          } ); //?? escaped?
23761         
23762         this.field = field.el.select('input', true).first();
23763         this.field.on("keydown", this.onPagingKeydown, this);
23764         this.field.on("focus", function(){this.dom.select();});
23765     
23766     
23767         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23768         //this.field.setHeight(18);
23769         //this.addSeparator();
23770         this.next = this.navgroup.addItem({
23771             tooltip: this.nextText,
23772             cls: "next",
23773             html : ' <i class="fa fa-step-forward">',
23774             disabled: true,
23775             preventDefault: true,
23776             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23777         });
23778         this.last = this.navgroup.addItem({
23779             tooltip: this.lastText,
23780             icon : 'fa fa-forward',
23781             cls: "next",
23782             disabled: true,
23783             preventDefault: true,
23784             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23785         });
23786     //this.addSeparator();
23787         this.loading = this.navgroup.addItem({
23788             tooltip: this.refreshText,
23789             icon: 'fa fa-refresh',
23790             preventDefault: true,
23791             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23792         });
23793         
23794     },
23795
23796     // private
23797     updateInfo : function(){
23798         if(this.displayEl){
23799             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23800             var msg = count == 0 ?
23801                 this.emptyMsg :
23802                 String.format(
23803                     this.displayMsg,
23804                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23805                 );
23806             this.displayEl.update(msg);
23807         }
23808     },
23809
23810     // private
23811     onLoad : function(ds, r, o){
23812        this.cursor = o.params ? o.params.start : 0;
23813        var d = this.getPageData(),
23814             ap = d.activePage,
23815             ps = d.pages;
23816         
23817        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23818        this.field.dom.value = ap;
23819        this.first.setDisabled(ap == 1);
23820        this.prev.setDisabled(ap == 1);
23821        this.next.setDisabled(ap == ps);
23822        this.last.setDisabled(ap == ps);
23823        this.loading.enable();
23824        this.updateInfo();
23825     },
23826
23827     // private
23828     getPageData : function(){
23829         var total = this.ds.getTotalCount();
23830         return {
23831             total : total,
23832             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23833             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23834         };
23835     },
23836
23837     // private
23838     onLoadError : function(){
23839         this.loading.enable();
23840     },
23841
23842     // private
23843     onPagingKeydown : function(e){
23844         var k = e.getKey();
23845         var d = this.getPageData();
23846         if(k == e.RETURN){
23847             var v = this.field.dom.value, pageNum;
23848             if(!v || isNaN(pageNum = parseInt(v, 10))){
23849                 this.field.dom.value = d.activePage;
23850                 return;
23851             }
23852             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23853             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23854             e.stopEvent();
23855         }
23856         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))
23857         {
23858           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23859           this.field.dom.value = pageNum;
23860           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23861           e.stopEvent();
23862         }
23863         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23864         {
23865           var v = this.field.dom.value, pageNum; 
23866           var increment = (e.shiftKey) ? 10 : 1;
23867           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23868                 increment *= -1;
23869           }
23870           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23871             this.field.dom.value = d.activePage;
23872             return;
23873           }
23874           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23875           {
23876             this.field.dom.value = parseInt(v, 10) + increment;
23877             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23878             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23879           }
23880           e.stopEvent();
23881         }
23882     },
23883
23884     // private
23885     beforeLoad : function(){
23886         if(this.loading){
23887             this.loading.disable();
23888         }
23889     },
23890
23891     // private
23892     onClick : function(which){
23893         
23894         var ds = this.ds;
23895         if (!ds) {
23896             return;
23897         }
23898         
23899         switch(which){
23900             case "first":
23901                 ds.load({params:{start: 0, limit: this.pageSize}});
23902             break;
23903             case "prev":
23904                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23905             break;
23906             case "next":
23907                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23908             break;
23909             case "last":
23910                 var total = ds.getTotalCount();
23911                 var extra = total % this.pageSize;
23912                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23913                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23914             break;
23915             case "refresh":
23916                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23917             break;
23918         }
23919     },
23920
23921     /**
23922      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23923      * @param {Roo.data.Store} store The data store to unbind
23924      */
23925     unbind : function(ds){
23926         ds.un("beforeload", this.beforeLoad, this);
23927         ds.un("load", this.onLoad, this);
23928         ds.un("loadexception", this.onLoadError, this);
23929         ds.un("remove", this.updateInfo, this);
23930         ds.un("add", this.updateInfo, this);
23931         this.ds = undefined;
23932     },
23933
23934     /**
23935      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23936      * @param {Roo.data.Store} store The data store to bind
23937      */
23938     bind : function(ds){
23939         ds.on("beforeload", this.beforeLoad, this);
23940         ds.on("load", this.onLoad, this);
23941         ds.on("loadexception", this.onLoadError, this);
23942         ds.on("remove", this.updateInfo, this);
23943         ds.on("add", this.updateInfo, this);
23944         this.ds = ds;
23945     }
23946 });/*
23947  * - LGPL
23948  *
23949  * element
23950  * 
23951  */
23952
23953 /**
23954  * @class Roo.bootstrap.MessageBar
23955  * @extends Roo.bootstrap.Component
23956  * Bootstrap MessageBar class
23957  * @cfg {String} html contents of the MessageBar
23958  * @cfg {String} weight (info | success | warning | danger) default info
23959  * @cfg {String} beforeClass insert the bar before the given class
23960  * @cfg {Boolean} closable (true | false) default false
23961  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23962  * 
23963  * @constructor
23964  * Create a new Element
23965  * @param {Object} config The config object
23966  */
23967
23968 Roo.bootstrap.MessageBar = function(config){
23969     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23970 };
23971
23972 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23973     
23974     html: '',
23975     weight: 'info',
23976     closable: false,
23977     fixed: false,
23978     beforeClass: 'bootstrap-sticky-wrap',
23979     
23980     getAutoCreate : function(){
23981         
23982         var cfg = {
23983             tag: 'div',
23984             cls: 'alert alert-dismissable alert-' + this.weight,
23985             cn: [
23986                 {
23987                     tag: 'span',
23988                     cls: 'message',
23989                     html: this.html || ''
23990                 }
23991             ]
23992         };
23993         
23994         if(this.fixed){
23995             cfg.cls += ' alert-messages-fixed';
23996         }
23997         
23998         if(this.closable){
23999             cfg.cn.push({
24000                 tag: 'button',
24001                 cls: 'close',
24002                 html: 'x'
24003             });
24004         }
24005         
24006         return cfg;
24007     },
24008     
24009     onRender : function(ct, position)
24010     {
24011         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24012         
24013         if(!this.el){
24014             var cfg = Roo.apply({},  this.getAutoCreate());
24015             cfg.id = Roo.id();
24016             
24017             if (this.cls) {
24018                 cfg.cls += ' ' + this.cls;
24019             }
24020             if (this.style) {
24021                 cfg.style = this.style;
24022             }
24023             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24024             
24025             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24026         }
24027         
24028         this.el.select('>button.close').on('click', this.hide, this);
24029         
24030     },
24031     
24032     show : function()
24033     {
24034         if (!this.rendered) {
24035             this.render();
24036         }
24037         
24038         this.el.show();
24039         
24040         this.fireEvent('show', this);
24041         
24042     },
24043     
24044     hide : function()
24045     {
24046         if (!this.rendered) {
24047             this.render();
24048         }
24049         
24050         this.el.hide();
24051         
24052         this.fireEvent('hide', this);
24053     },
24054     
24055     update : function()
24056     {
24057 //        var e = this.el.dom.firstChild;
24058 //        
24059 //        if(this.closable){
24060 //            e = e.nextSibling;
24061 //        }
24062 //        
24063 //        e.data = this.html || '';
24064
24065         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24066     }
24067    
24068 });
24069
24070  
24071
24072      /*
24073  * - LGPL
24074  *
24075  * Graph
24076  * 
24077  */
24078
24079
24080 /**
24081  * @class Roo.bootstrap.Graph
24082  * @extends Roo.bootstrap.Component
24083  * Bootstrap Graph class
24084 > Prameters
24085  -sm {number} sm 4
24086  -md {number} md 5
24087  @cfg {String} graphtype  bar | vbar | pie
24088  @cfg {number} g_x coodinator | centre x (pie)
24089  @cfg {number} g_y coodinator | centre y (pie)
24090  @cfg {number} g_r radius (pie)
24091  @cfg {number} g_height height of the chart (respected by all elements in the set)
24092  @cfg {number} g_width width of the chart (respected by all elements in the set)
24093  @cfg {Object} title The title of the chart
24094     
24095  -{Array}  values
24096  -opts (object) options for the chart 
24097      o {
24098      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24099      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24100      o vgutter (number)
24101      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.
24102      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24103      o to
24104      o stretch (boolean)
24105      o }
24106  -opts (object) options for the pie
24107      o{
24108      o cut
24109      o startAngle (number)
24110      o endAngle (number)
24111      } 
24112  *
24113  * @constructor
24114  * Create a new Input
24115  * @param {Object} config The config object
24116  */
24117
24118 Roo.bootstrap.Graph = function(config){
24119     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24120     
24121     this.addEvents({
24122         // img events
24123         /**
24124          * @event click
24125          * The img click event for the img.
24126          * @param {Roo.EventObject} e
24127          */
24128         "click" : true
24129     });
24130 };
24131
24132 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24133     
24134     sm: 4,
24135     md: 5,
24136     graphtype: 'bar',
24137     g_height: 250,
24138     g_width: 400,
24139     g_x: 50,
24140     g_y: 50,
24141     g_r: 30,
24142     opts:{
24143         //g_colors: this.colors,
24144         g_type: 'soft',
24145         g_gutter: '20%'
24146
24147     },
24148     title : false,
24149
24150     getAutoCreate : function(){
24151         
24152         var cfg = {
24153             tag: 'div',
24154             html : null
24155         };
24156         
24157         
24158         return  cfg;
24159     },
24160
24161     onRender : function(ct,position){
24162         
24163         
24164         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24165         
24166         if (typeof(Raphael) == 'undefined') {
24167             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24168             return;
24169         }
24170         
24171         this.raphael = Raphael(this.el.dom);
24172         
24173                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24174                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24175                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24176                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24177                 /*
24178                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24179                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24180                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24181                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24182                 
24183                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24184                 r.barchart(330, 10, 300, 220, data1);
24185                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24186                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24187                 */
24188                 
24189                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24190                 // r.barchart(30, 30, 560, 250,  xdata, {
24191                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24192                 //     axis : "0 0 1 1",
24193                 //     axisxlabels :  xdata
24194                 //     //yvalues : cols,
24195                    
24196                 // });
24197 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24198 //        
24199 //        this.load(null,xdata,{
24200 //                axis : "0 0 1 1",
24201 //                axisxlabels :  xdata
24202 //                });
24203
24204     },
24205
24206     load : function(graphtype,xdata,opts)
24207     {
24208         this.raphael.clear();
24209         if(!graphtype) {
24210             graphtype = this.graphtype;
24211         }
24212         if(!opts){
24213             opts = this.opts;
24214         }
24215         var r = this.raphael,
24216             fin = function () {
24217                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24218             },
24219             fout = function () {
24220                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24221             },
24222             pfin = function() {
24223                 this.sector.stop();
24224                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24225
24226                 if (this.label) {
24227                     this.label[0].stop();
24228                     this.label[0].attr({ r: 7.5 });
24229                     this.label[1].attr({ "font-weight": 800 });
24230                 }
24231             },
24232             pfout = function() {
24233                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24234
24235                 if (this.label) {
24236                     this.label[0].animate({ r: 5 }, 500, "bounce");
24237                     this.label[1].attr({ "font-weight": 400 });
24238                 }
24239             };
24240
24241         switch(graphtype){
24242             case 'bar':
24243                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24244                 break;
24245             case 'hbar':
24246                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24247                 break;
24248             case 'pie':
24249 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24250 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24251 //            
24252                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24253                 
24254                 break;
24255
24256         }
24257         
24258         if(this.title){
24259             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24260         }
24261         
24262     },
24263     
24264     setTitle: function(o)
24265     {
24266         this.title = o;
24267     },
24268     
24269     initEvents: function() {
24270         
24271         if(!this.href){
24272             this.el.on('click', this.onClick, this);
24273         }
24274     },
24275     
24276     onClick : function(e)
24277     {
24278         Roo.log('img onclick');
24279         this.fireEvent('click', this, e);
24280     }
24281    
24282 });
24283
24284  
24285 /*
24286  * - LGPL
24287  *
24288  * numberBox
24289  * 
24290  */
24291 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24292
24293 /**
24294  * @class Roo.bootstrap.dash.NumberBox
24295  * @extends Roo.bootstrap.Component
24296  * Bootstrap NumberBox class
24297  * @cfg {String} headline Box headline
24298  * @cfg {String} content Box content
24299  * @cfg {String} icon Box icon
24300  * @cfg {String} footer Footer text
24301  * @cfg {String} fhref Footer href
24302  * 
24303  * @constructor
24304  * Create a new NumberBox
24305  * @param {Object} config The config object
24306  */
24307
24308
24309 Roo.bootstrap.dash.NumberBox = function(config){
24310     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24311     
24312 };
24313
24314 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24315     
24316     headline : '',
24317     content : '',
24318     icon : '',
24319     footer : '',
24320     fhref : '',
24321     ficon : '',
24322     
24323     getAutoCreate : function(){
24324         
24325         var cfg = {
24326             tag : 'div',
24327             cls : 'small-box ',
24328             cn : [
24329                 {
24330                     tag : 'div',
24331                     cls : 'inner',
24332                     cn :[
24333                         {
24334                             tag : 'h3',
24335                             cls : 'roo-headline',
24336                             html : this.headline
24337                         },
24338                         {
24339                             tag : 'p',
24340                             cls : 'roo-content',
24341                             html : this.content
24342                         }
24343                     ]
24344                 }
24345             ]
24346         };
24347         
24348         if(this.icon){
24349             cfg.cn.push({
24350                 tag : 'div',
24351                 cls : 'icon',
24352                 cn :[
24353                     {
24354                         tag : 'i',
24355                         cls : 'ion ' + this.icon
24356                     }
24357                 ]
24358             });
24359         }
24360         
24361         if(this.footer){
24362             var footer = {
24363                 tag : 'a',
24364                 cls : 'small-box-footer',
24365                 href : this.fhref || '#',
24366                 html : this.footer
24367             };
24368             
24369             cfg.cn.push(footer);
24370             
24371         }
24372         
24373         return  cfg;
24374     },
24375
24376     onRender : function(ct,position){
24377         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24378
24379
24380        
24381                 
24382     },
24383
24384     setHeadline: function (value)
24385     {
24386         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24387     },
24388     
24389     setFooter: function (value, href)
24390     {
24391         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24392         
24393         if(href){
24394             this.el.select('a.small-box-footer',true).first().attr('href', href);
24395         }
24396         
24397     },
24398
24399     setContent: function (value)
24400     {
24401         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24402     },
24403
24404     initEvents: function() 
24405     {   
24406         
24407     }
24408     
24409 });
24410
24411  
24412 /*
24413  * - LGPL
24414  *
24415  * TabBox
24416  * 
24417  */
24418 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24419
24420 /**
24421  * @class Roo.bootstrap.dash.TabBox
24422  * @extends Roo.bootstrap.Component
24423  * Bootstrap TabBox class
24424  * @cfg {String} title Title of the TabBox
24425  * @cfg {String} icon Icon of the TabBox
24426  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24427  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24428  * 
24429  * @constructor
24430  * Create a new TabBox
24431  * @param {Object} config The config object
24432  */
24433
24434
24435 Roo.bootstrap.dash.TabBox = function(config){
24436     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24437     this.addEvents({
24438         // raw events
24439         /**
24440          * @event addpane
24441          * When a pane is added
24442          * @param {Roo.bootstrap.dash.TabPane} pane
24443          */
24444         "addpane" : true,
24445         /**
24446          * @event activatepane
24447          * When a pane is activated
24448          * @param {Roo.bootstrap.dash.TabPane} pane
24449          */
24450         "activatepane" : true
24451         
24452          
24453     });
24454     
24455     this.panes = [];
24456 };
24457
24458 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24459
24460     title : '',
24461     icon : false,
24462     showtabs : true,
24463     tabScrollable : false,
24464     
24465     getChildContainer : function()
24466     {
24467         return this.el.select('.tab-content', true).first();
24468     },
24469     
24470     getAutoCreate : function(){
24471         
24472         var header = {
24473             tag: 'li',
24474             cls: 'pull-left header',
24475             html: this.title,
24476             cn : []
24477         };
24478         
24479         if(this.icon){
24480             header.cn.push({
24481                 tag: 'i',
24482                 cls: 'fa ' + this.icon
24483             });
24484         }
24485         
24486         var h = {
24487             tag: 'ul',
24488             cls: 'nav nav-tabs pull-right',
24489             cn: [
24490                 header
24491             ]
24492         };
24493         
24494         if(this.tabScrollable){
24495             h = {
24496                 tag: 'div',
24497                 cls: 'tab-header',
24498                 cn: [
24499                     {
24500                         tag: 'ul',
24501                         cls: 'nav nav-tabs pull-right',
24502                         cn: [
24503                             header
24504                         ]
24505                     }
24506                 ]
24507             };
24508         }
24509         
24510         var cfg = {
24511             tag: 'div',
24512             cls: 'nav-tabs-custom',
24513             cn: [
24514                 h,
24515                 {
24516                     tag: 'div',
24517                     cls: 'tab-content no-padding',
24518                     cn: []
24519                 }
24520             ]
24521         };
24522
24523         return  cfg;
24524     },
24525     initEvents : function()
24526     {
24527         //Roo.log('add add pane handler');
24528         this.on('addpane', this.onAddPane, this);
24529     },
24530      /**
24531      * Updates the box title
24532      * @param {String} html to set the title to.
24533      */
24534     setTitle : function(value)
24535     {
24536         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24537     },
24538     onAddPane : function(pane)
24539     {
24540         this.panes.push(pane);
24541         //Roo.log('addpane');
24542         //Roo.log(pane);
24543         // tabs are rendere left to right..
24544         if(!this.showtabs){
24545             return;
24546         }
24547         
24548         var ctr = this.el.select('.nav-tabs', true).first();
24549          
24550          
24551         var existing = ctr.select('.nav-tab',true);
24552         var qty = existing.getCount();;
24553         
24554         
24555         var tab = ctr.createChild({
24556             tag : 'li',
24557             cls : 'nav-tab' + (qty ? '' : ' active'),
24558             cn : [
24559                 {
24560                     tag : 'a',
24561                     href:'#',
24562                     html : pane.title
24563                 }
24564             ]
24565         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24566         pane.tab = tab;
24567         
24568         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24569         if (!qty) {
24570             pane.el.addClass('active');
24571         }
24572         
24573                 
24574     },
24575     onTabClick : function(ev,un,ob,pane)
24576     {
24577         //Roo.log('tab - prev default');
24578         ev.preventDefault();
24579         
24580         
24581         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24582         pane.tab.addClass('active');
24583         //Roo.log(pane.title);
24584         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24585         // technically we should have a deactivate event.. but maybe add later.
24586         // and it should not de-activate the selected tab...
24587         this.fireEvent('activatepane', pane);
24588         pane.el.addClass('active');
24589         pane.fireEvent('activate');
24590         
24591         
24592     },
24593     
24594     getActivePane : function()
24595     {
24596         var r = false;
24597         Roo.each(this.panes, function(p) {
24598             if(p.el.hasClass('active')){
24599                 r = p;
24600                 return false;
24601             }
24602             
24603             return;
24604         });
24605         
24606         return r;
24607     }
24608     
24609     
24610 });
24611
24612  
24613 /*
24614  * - LGPL
24615  *
24616  * Tab pane
24617  * 
24618  */
24619 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24620 /**
24621  * @class Roo.bootstrap.TabPane
24622  * @extends Roo.bootstrap.Component
24623  * Bootstrap TabPane class
24624  * @cfg {Boolean} active (false | true) Default false
24625  * @cfg {String} title title of panel
24626
24627  * 
24628  * @constructor
24629  * Create a new TabPane
24630  * @param {Object} config The config object
24631  */
24632
24633 Roo.bootstrap.dash.TabPane = function(config){
24634     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24635     
24636     this.addEvents({
24637         // raw events
24638         /**
24639          * @event activate
24640          * When a pane is activated
24641          * @param {Roo.bootstrap.dash.TabPane} pane
24642          */
24643         "activate" : true
24644          
24645     });
24646 };
24647
24648 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24649     
24650     active : false,
24651     title : '',
24652     
24653     // the tabBox that this is attached to.
24654     tab : false,
24655      
24656     getAutoCreate : function() 
24657     {
24658         var cfg = {
24659             tag: 'div',
24660             cls: 'tab-pane'
24661         };
24662         
24663         if(this.active){
24664             cfg.cls += ' active';
24665         }
24666         
24667         return cfg;
24668     },
24669     initEvents  : function()
24670     {
24671         //Roo.log('trigger add pane handler');
24672         this.parent().fireEvent('addpane', this)
24673     },
24674     
24675      /**
24676      * Updates the tab title 
24677      * @param {String} html to set the title to.
24678      */
24679     setTitle: function(str)
24680     {
24681         if (!this.tab) {
24682             return;
24683         }
24684         this.title = str;
24685         this.tab.select('a', true).first().dom.innerHTML = str;
24686         
24687     }
24688     
24689     
24690     
24691 });
24692
24693  
24694
24695
24696  /*
24697  * - LGPL
24698  *
24699  * menu
24700  * 
24701  */
24702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24703
24704 /**
24705  * @class Roo.bootstrap.menu.Menu
24706  * @extends Roo.bootstrap.Component
24707  * Bootstrap Menu class - container for Menu
24708  * @cfg {String} html Text of the menu
24709  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24710  * @cfg {String} icon Font awesome icon
24711  * @cfg {String} pos Menu align to (top | bottom) default bottom
24712  * 
24713  * 
24714  * @constructor
24715  * Create a new Menu
24716  * @param {Object} config The config object
24717  */
24718
24719
24720 Roo.bootstrap.menu.Menu = function(config){
24721     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24722     
24723     this.addEvents({
24724         /**
24725          * @event beforeshow
24726          * Fires before this menu is displayed
24727          * @param {Roo.bootstrap.menu.Menu} this
24728          */
24729         beforeshow : true,
24730         /**
24731          * @event beforehide
24732          * Fires before this menu is hidden
24733          * @param {Roo.bootstrap.menu.Menu} this
24734          */
24735         beforehide : true,
24736         /**
24737          * @event show
24738          * Fires after this menu is displayed
24739          * @param {Roo.bootstrap.menu.Menu} this
24740          */
24741         show : true,
24742         /**
24743          * @event hide
24744          * Fires after this menu is hidden
24745          * @param {Roo.bootstrap.menu.Menu} this
24746          */
24747         hide : true,
24748         /**
24749          * @event click
24750          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24751          * @param {Roo.bootstrap.menu.Menu} this
24752          * @param {Roo.EventObject} e
24753          */
24754         click : true
24755     });
24756     
24757 };
24758
24759 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24760     
24761     submenu : false,
24762     html : '',
24763     weight : 'default',
24764     icon : false,
24765     pos : 'bottom',
24766     
24767     
24768     getChildContainer : function() {
24769         if(this.isSubMenu){
24770             return this.el;
24771         }
24772         
24773         return this.el.select('ul.dropdown-menu', true).first();  
24774     },
24775     
24776     getAutoCreate : function()
24777     {
24778         var text = [
24779             {
24780                 tag : 'span',
24781                 cls : 'roo-menu-text',
24782                 html : this.html
24783             }
24784         ];
24785         
24786         if(this.icon){
24787             text.unshift({
24788                 tag : 'i',
24789                 cls : 'fa ' + this.icon
24790             })
24791         }
24792         
24793         
24794         var cfg = {
24795             tag : 'div',
24796             cls : 'btn-group',
24797             cn : [
24798                 {
24799                     tag : 'button',
24800                     cls : 'dropdown-button btn btn-' + this.weight,
24801                     cn : text
24802                 },
24803                 {
24804                     tag : 'button',
24805                     cls : 'dropdown-toggle btn btn-' + this.weight,
24806                     cn : [
24807                         {
24808                             tag : 'span',
24809                             cls : 'caret'
24810                         }
24811                     ]
24812                 },
24813                 {
24814                     tag : 'ul',
24815                     cls : 'dropdown-menu'
24816                 }
24817             ]
24818             
24819         };
24820         
24821         if(this.pos == 'top'){
24822             cfg.cls += ' dropup';
24823         }
24824         
24825         if(this.isSubMenu){
24826             cfg = {
24827                 tag : 'ul',
24828                 cls : 'dropdown-menu'
24829             }
24830         }
24831         
24832         return cfg;
24833     },
24834     
24835     onRender : function(ct, position)
24836     {
24837         this.isSubMenu = ct.hasClass('dropdown-submenu');
24838         
24839         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24840     },
24841     
24842     initEvents : function() 
24843     {
24844         if(this.isSubMenu){
24845             return;
24846         }
24847         
24848         this.hidden = true;
24849         
24850         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24851         this.triggerEl.on('click', this.onTriggerPress, this);
24852         
24853         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24854         this.buttonEl.on('click', this.onClick, this);
24855         
24856     },
24857     
24858     list : function()
24859     {
24860         if(this.isSubMenu){
24861             return this.el;
24862         }
24863         
24864         return this.el.select('ul.dropdown-menu', true).first();
24865     },
24866     
24867     onClick : function(e)
24868     {
24869         this.fireEvent("click", this, e);
24870     },
24871     
24872     onTriggerPress  : function(e)
24873     {   
24874         if (this.isVisible()) {
24875             this.hide();
24876         } else {
24877             this.show();
24878         }
24879     },
24880     
24881     isVisible : function(){
24882         return !this.hidden;
24883     },
24884     
24885     show : function()
24886     {
24887         this.fireEvent("beforeshow", this);
24888         
24889         this.hidden = false;
24890         this.el.addClass('open');
24891         
24892         Roo.get(document).on("mouseup", this.onMouseUp, this);
24893         
24894         this.fireEvent("show", this);
24895         
24896         
24897     },
24898     
24899     hide : function()
24900     {
24901         this.fireEvent("beforehide", this);
24902         
24903         this.hidden = true;
24904         this.el.removeClass('open');
24905         
24906         Roo.get(document).un("mouseup", this.onMouseUp);
24907         
24908         this.fireEvent("hide", this);
24909     },
24910     
24911     onMouseUp : function()
24912     {
24913         this.hide();
24914     }
24915     
24916 });
24917
24918  
24919  /*
24920  * - LGPL
24921  *
24922  * menu item
24923  * 
24924  */
24925 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24926
24927 /**
24928  * @class Roo.bootstrap.menu.Item
24929  * @extends Roo.bootstrap.Component
24930  * Bootstrap MenuItem class
24931  * @cfg {Boolean} submenu (true | false) default false
24932  * @cfg {String} html text of the item
24933  * @cfg {String} href the link
24934  * @cfg {Boolean} disable (true | false) default false
24935  * @cfg {Boolean} preventDefault (true | false) default true
24936  * @cfg {String} icon Font awesome icon
24937  * @cfg {String} pos Submenu align to (left | right) default right 
24938  * 
24939  * 
24940  * @constructor
24941  * Create a new Item
24942  * @param {Object} config The config object
24943  */
24944
24945
24946 Roo.bootstrap.menu.Item = function(config){
24947     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24948     this.addEvents({
24949         /**
24950          * @event mouseover
24951          * Fires when the mouse is hovering over this menu
24952          * @param {Roo.bootstrap.menu.Item} this
24953          * @param {Roo.EventObject} e
24954          */
24955         mouseover : true,
24956         /**
24957          * @event mouseout
24958          * Fires when the mouse exits this menu
24959          * @param {Roo.bootstrap.menu.Item} this
24960          * @param {Roo.EventObject} e
24961          */
24962         mouseout : true,
24963         // raw events
24964         /**
24965          * @event click
24966          * The raw click event for the entire grid.
24967          * @param {Roo.EventObject} e
24968          */
24969         click : true
24970     });
24971 };
24972
24973 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24974     
24975     submenu : false,
24976     href : '',
24977     html : '',
24978     preventDefault: true,
24979     disable : false,
24980     icon : false,
24981     pos : 'right',
24982     
24983     getAutoCreate : function()
24984     {
24985         var text = [
24986             {
24987                 tag : 'span',
24988                 cls : 'roo-menu-item-text',
24989                 html : this.html
24990             }
24991         ];
24992         
24993         if(this.icon){
24994             text.unshift({
24995                 tag : 'i',
24996                 cls : 'fa ' + this.icon
24997             })
24998         }
24999         
25000         var cfg = {
25001             tag : 'li',
25002             cn : [
25003                 {
25004                     tag : 'a',
25005                     href : this.href || '#',
25006                     cn : text
25007                 }
25008             ]
25009         };
25010         
25011         if(this.disable){
25012             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25013         }
25014         
25015         if(this.submenu){
25016             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25017             
25018             if(this.pos == 'left'){
25019                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25020             }
25021         }
25022         
25023         return cfg;
25024     },
25025     
25026     initEvents : function() 
25027     {
25028         this.el.on('mouseover', this.onMouseOver, this);
25029         this.el.on('mouseout', this.onMouseOut, this);
25030         
25031         this.el.select('a', true).first().on('click', this.onClick, this);
25032         
25033     },
25034     
25035     onClick : function(e)
25036     {
25037         if(this.preventDefault){
25038             e.preventDefault();
25039         }
25040         
25041         this.fireEvent("click", this, e);
25042     },
25043     
25044     onMouseOver : function(e)
25045     {
25046         if(this.submenu && this.pos == 'left'){
25047             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25048         }
25049         
25050         this.fireEvent("mouseover", this, e);
25051     },
25052     
25053     onMouseOut : function(e)
25054     {
25055         this.fireEvent("mouseout", this, e);
25056     }
25057 });
25058
25059  
25060
25061  /*
25062  * - LGPL
25063  *
25064  * menu separator
25065  * 
25066  */
25067 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25068
25069 /**
25070  * @class Roo.bootstrap.menu.Separator
25071  * @extends Roo.bootstrap.Component
25072  * Bootstrap Separator class
25073  * 
25074  * @constructor
25075  * Create a new Separator
25076  * @param {Object} config The config object
25077  */
25078
25079
25080 Roo.bootstrap.menu.Separator = function(config){
25081     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25082 };
25083
25084 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25085     
25086     getAutoCreate : function(){
25087         var cfg = {
25088             tag : 'li',
25089             cls: 'divider'
25090         };
25091         
25092         return cfg;
25093     }
25094    
25095 });
25096
25097  
25098
25099  /*
25100  * - LGPL
25101  *
25102  * Tooltip
25103  * 
25104  */
25105
25106 /**
25107  * @class Roo.bootstrap.Tooltip
25108  * Bootstrap Tooltip class
25109  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25110  * to determine which dom element triggers the tooltip.
25111  * 
25112  * It needs to add support for additional attributes like tooltip-position
25113  * 
25114  * @constructor
25115  * Create a new Toolti
25116  * @param {Object} config The config object
25117  */
25118
25119 Roo.bootstrap.Tooltip = function(config){
25120     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25121     
25122     this.alignment = Roo.bootstrap.Tooltip.alignment;
25123     
25124     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25125         this.alignment = config.alignment;
25126     }
25127     
25128 };
25129
25130 Roo.apply(Roo.bootstrap.Tooltip, {
25131     /**
25132      * @function init initialize tooltip monitoring.
25133      * @static
25134      */
25135     currentEl : false,
25136     currentTip : false,
25137     currentRegion : false,
25138     
25139     //  init : delay?
25140     
25141     init : function()
25142     {
25143         Roo.get(document).on('mouseover', this.enter ,this);
25144         Roo.get(document).on('mouseout', this.leave, this);
25145          
25146         
25147         this.currentTip = new Roo.bootstrap.Tooltip();
25148     },
25149     
25150     enter : function(ev)
25151     {
25152         var dom = ev.getTarget();
25153         
25154         //Roo.log(['enter',dom]);
25155         var el = Roo.fly(dom);
25156         if (this.currentEl) {
25157             //Roo.log(dom);
25158             //Roo.log(this.currentEl);
25159             //Roo.log(this.currentEl.contains(dom));
25160             if (this.currentEl == el) {
25161                 return;
25162             }
25163             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25164                 return;
25165             }
25166
25167         }
25168         
25169         if (this.currentTip.el) {
25170             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25171         }    
25172         //Roo.log(ev);
25173         
25174         if(!el || el.dom == document){
25175             return;
25176         }
25177         
25178         var bindEl = el;
25179         
25180         // you can not look for children, as if el is the body.. then everythign is the child..
25181         if (!el.attr('tooltip')) { //
25182             if (!el.select("[tooltip]").elements.length) {
25183                 return;
25184             }
25185             // is the mouse over this child...?
25186             bindEl = el.select("[tooltip]").first();
25187             var xy = ev.getXY();
25188             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25189                 //Roo.log("not in region.");
25190                 return;
25191             }
25192             //Roo.log("child element over..");
25193             
25194         }
25195         this.currentEl = bindEl;
25196         this.currentTip.bind(bindEl);
25197         this.currentRegion = Roo.lib.Region.getRegion(dom);
25198         this.currentTip.enter();
25199         
25200     },
25201     leave : function(ev)
25202     {
25203         var dom = ev.getTarget();
25204         //Roo.log(['leave',dom]);
25205         if (!this.currentEl) {
25206             return;
25207         }
25208         
25209         
25210         if (dom != this.currentEl.dom) {
25211             return;
25212         }
25213         var xy = ev.getXY();
25214         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25215             return;
25216         }
25217         // only activate leave if mouse cursor is outside... bounding box..
25218         
25219         
25220         
25221         
25222         if (this.currentTip) {
25223             this.currentTip.leave();
25224         }
25225         //Roo.log('clear currentEl');
25226         this.currentEl = false;
25227         
25228         
25229     },
25230     alignment : {
25231         'left' : ['r-l', [-2,0], 'right'],
25232         'right' : ['l-r', [2,0], 'left'],
25233         'bottom' : ['t-b', [0,2], 'top'],
25234         'top' : [ 'b-t', [0,-2], 'bottom']
25235     }
25236     
25237 });
25238
25239
25240 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25241     
25242     
25243     bindEl : false,
25244     
25245     delay : null, // can be { show : 300 , hide: 500}
25246     
25247     timeout : null,
25248     
25249     hoverState : null, //???
25250     
25251     placement : 'bottom', 
25252     
25253     alignment : false,
25254     
25255     getAutoCreate : function(){
25256     
25257         var cfg = {
25258            cls : 'tooltip',
25259            role : 'tooltip',
25260            cn : [
25261                 {
25262                     cls : 'tooltip-arrow'
25263                 },
25264                 {
25265                     cls : 'tooltip-inner'
25266                 }
25267            ]
25268         };
25269         
25270         return cfg;
25271     },
25272     bind : function(el)
25273     {
25274         this.bindEl = el;
25275     },
25276       
25277     
25278     enter : function () {
25279        
25280         if (this.timeout != null) {
25281             clearTimeout(this.timeout);
25282         }
25283         
25284         this.hoverState = 'in';
25285          //Roo.log("enter - show");
25286         if (!this.delay || !this.delay.show) {
25287             this.show();
25288             return;
25289         }
25290         var _t = this;
25291         this.timeout = setTimeout(function () {
25292             if (_t.hoverState == 'in') {
25293                 _t.show();
25294             }
25295         }, this.delay.show);
25296     },
25297     leave : function()
25298     {
25299         clearTimeout(this.timeout);
25300     
25301         this.hoverState = 'out';
25302          if (!this.delay || !this.delay.hide) {
25303             this.hide();
25304             return;
25305         }
25306        
25307         var _t = this;
25308         this.timeout = setTimeout(function () {
25309             //Roo.log("leave - timeout");
25310             
25311             if (_t.hoverState == 'out') {
25312                 _t.hide();
25313                 Roo.bootstrap.Tooltip.currentEl = false;
25314             }
25315         }, delay);
25316     },
25317     
25318     show : function (msg)
25319     {
25320         if (!this.el) {
25321             this.render(document.body);
25322         }
25323         // set content.
25324         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25325         
25326         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25327         
25328         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25329         
25330         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25331         
25332         var placement = typeof this.placement == 'function' ?
25333             this.placement.call(this, this.el, on_el) :
25334             this.placement;
25335             
25336         var autoToken = /\s?auto?\s?/i;
25337         var autoPlace = autoToken.test(placement);
25338         if (autoPlace) {
25339             placement = placement.replace(autoToken, '') || 'top';
25340         }
25341         
25342         //this.el.detach()
25343         //this.el.setXY([0,0]);
25344         this.el.show();
25345         //this.el.dom.style.display='block';
25346         
25347         //this.el.appendTo(on_el);
25348         
25349         var p = this.getPosition();
25350         var box = this.el.getBox();
25351         
25352         if (autoPlace) {
25353             // fixme..
25354         }
25355         
25356         var align = this.alignment[placement];
25357         
25358         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25359         
25360         if(placement == 'top' || placement == 'bottom'){
25361             if(xy[0] < 0){
25362                 placement = 'right';
25363             }
25364             
25365             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25366                 placement = 'left';
25367             }
25368             
25369             var scroll = Roo.select('body', true).first().getScroll();
25370             
25371             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25372                 placement = 'top';
25373             }
25374             
25375         }
25376         
25377         this.el.alignTo(this.bindEl, align[0],align[1]);
25378         //var arrow = this.el.select('.arrow',true).first();
25379         //arrow.set(align[2], 
25380         
25381         this.el.addClass(placement);
25382         
25383         this.el.addClass('in fade');
25384         
25385         this.hoverState = null;
25386         
25387         if (this.el.hasClass('fade')) {
25388             // fade it?
25389         }
25390         
25391     },
25392     hide : function()
25393     {
25394          
25395         if (!this.el) {
25396             return;
25397         }
25398         //this.el.setXY([0,0]);
25399         this.el.removeClass('in');
25400         //this.el.hide();
25401         
25402     }
25403     
25404 });
25405  
25406
25407  /*
25408  * - LGPL
25409  *
25410  * Location Picker
25411  * 
25412  */
25413
25414 /**
25415  * @class Roo.bootstrap.LocationPicker
25416  * @extends Roo.bootstrap.Component
25417  * Bootstrap LocationPicker class
25418  * @cfg {Number} latitude Position when init default 0
25419  * @cfg {Number} longitude Position when init default 0
25420  * @cfg {Number} zoom default 15
25421  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25422  * @cfg {Boolean} mapTypeControl default false
25423  * @cfg {Boolean} disableDoubleClickZoom default false
25424  * @cfg {Boolean} scrollwheel default true
25425  * @cfg {Boolean} streetViewControl default false
25426  * @cfg {Number} radius default 0
25427  * @cfg {String} locationName
25428  * @cfg {Boolean} draggable default true
25429  * @cfg {Boolean} enableAutocomplete default false
25430  * @cfg {Boolean} enableReverseGeocode default true
25431  * @cfg {String} markerTitle
25432  * 
25433  * @constructor
25434  * Create a new LocationPicker
25435  * @param {Object} config The config object
25436  */
25437
25438
25439 Roo.bootstrap.LocationPicker = function(config){
25440     
25441     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25442     
25443     this.addEvents({
25444         /**
25445          * @event initial
25446          * Fires when the picker initialized.
25447          * @param {Roo.bootstrap.LocationPicker} this
25448          * @param {Google Location} location
25449          */
25450         initial : true,
25451         /**
25452          * @event positionchanged
25453          * Fires when the picker position changed.
25454          * @param {Roo.bootstrap.LocationPicker} this
25455          * @param {Google Location} location
25456          */
25457         positionchanged : true,
25458         /**
25459          * @event resize
25460          * Fires when the map resize.
25461          * @param {Roo.bootstrap.LocationPicker} this
25462          */
25463         resize : true,
25464         /**
25465          * @event show
25466          * Fires when the map show.
25467          * @param {Roo.bootstrap.LocationPicker} this
25468          */
25469         show : true,
25470         /**
25471          * @event hide
25472          * Fires when the map hide.
25473          * @param {Roo.bootstrap.LocationPicker} this
25474          */
25475         hide : true,
25476         /**
25477          * @event mapClick
25478          * Fires when click the map.
25479          * @param {Roo.bootstrap.LocationPicker} this
25480          * @param {Map event} e
25481          */
25482         mapClick : true,
25483         /**
25484          * @event mapRightClick
25485          * Fires when right click the map.
25486          * @param {Roo.bootstrap.LocationPicker} this
25487          * @param {Map event} e
25488          */
25489         mapRightClick : true,
25490         /**
25491          * @event markerClick
25492          * Fires when click the marker.
25493          * @param {Roo.bootstrap.LocationPicker} this
25494          * @param {Map event} e
25495          */
25496         markerClick : true,
25497         /**
25498          * @event markerRightClick
25499          * Fires when right click the marker.
25500          * @param {Roo.bootstrap.LocationPicker} this
25501          * @param {Map event} e
25502          */
25503         markerRightClick : true,
25504         /**
25505          * @event OverlayViewDraw
25506          * Fires when OverlayView Draw
25507          * @param {Roo.bootstrap.LocationPicker} this
25508          */
25509         OverlayViewDraw : true,
25510         /**
25511          * @event OverlayViewOnAdd
25512          * Fires when OverlayView Draw
25513          * @param {Roo.bootstrap.LocationPicker} this
25514          */
25515         OverlayViewOnAdd : true,
25516         /**
25517          * @event OverlayViewOnRemove
25518          * Fires when OverlayView Draw
25519          * @param {Roo.bootstrap.LocationPicker} this
25520          */
25521         OverlayViewOnRemove : true,
25522         /**
25523          * @event OverlayViewShow
25524          * Fires when OverlayView Draw
25525          * @param {Roo.bootstrap.LocationPicker} this
25526          * @param {Pixel} cpx
25527          */
25528         OverlayViewShow : true,
25529         /**
25530          * @event OverlayViewHide
25531          * Fires when OverlayView Draw
25532          * @param {Roo.bootstrap.LocationPicker} this
25533          */
25534         OverlayViewHide : true,
25535         /**
25536          * @event loadexception
25537          * Fires when load google lib failed.
25538          * @param {Roo.bootstrap.LocationPicker} this
25539          */
25540         loadexception : true
25541     });
25542         
25543 };
25544
25545 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25546     
25547     gMapContext: false,
25548     
25549     latitude: 0,
25550     longitude: 0,
25551     zoom: 15,
25552     mapTypeId: false,
25553     mapTypeControl: false,
25554     disableDoubleClickZoom: false,
25555     scrollwheel: true,
25556     streetViewControl: false,
25557     radius: 0,
25558     locationName: '',
25559     draggable: true,
25560     enableAutocomplete: false,
25561     enableReverseGeocode: true,
25562     markerTitle: '',
25563     
25564     getAutoCreate: function()
25565     {
25566
25567         var cfg = {
25568             tag: 'div',
25569             cls: 'roo-location-picker'
25570         };
25571         
25572         return cfg
25573     },
25574     
25575     initEvents: function(ct, position)
25576     {       
25577         if(!this.el.getWidth() || this.isApplied()){
25578             return;
25579         }
25580         
25581         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25582         
25583         this.initial();
25584     },
25585     
25586     initial: function()
25587     {
25588         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25589             this.fireEvent('loadexception', this);
25590             return;
25591         }
25592         
25593         if(!this.mapTypeId){
25594             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25595         }
25596         
25597         this.gMapContext = this.GMapContext();
25598         
25599         this.initOverlayView();
25600         
25601         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25602         
25603         var _this = this;
25604                 
25605         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25606             _this.setPosition(_this.gMapContext.marker.position);
25607         });
25608         
25609         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25610             _this.fireEvent('mapClick', this, event);
25611             
25612         });
25613
25614         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25615             _this.fireEvent('mapRightClick', this, event);
25616             
25617         });
25618         
25619         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25620             _this.fireEvent('markerClick', this, event);
25621             
25622         });
25623
25624         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25625             _this.fireEvent('markerRightClick', this, event);
25626             
25627         });
25628         
25629         this.setPosition(this.gMapContext.location);
25630         
25631         this.fireEvent('initial', this, this.gMapContext.location);
25632     },
25633     
25634     initOverlayView: function()
25635     {
25636         var _this = this;
25637         
25638         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25639             
25640             draw: function()
25641             {
25642                 _this.fireEvent('OverlayViewDraw', _this);
25643             },
25644             
25645             onAdd: function()
25646             {
25647                 _this.fireEvent('OverlayViewOnAdd', _this);
25648             },
25649             
25650             onRemove: function()
25651             {
25652                 _this.fireEvent('OverlayViewOnRemove', _this);
25653             },
25654             
25655             show: function(cpx)
25656             {
25657                 _this.fireEvent('OverlayViewShow', _this, cpx);
25658             },
25659             
25660             hide: function()
25661             {
25662                 _this.fireEvent('OverlayViewHide', _this);
25663             }
25664             
25665         });
25666     },
25667     
25668     fromLatLngToContainerPixel: function(event)
25669     {
25670         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25671     },
25672     
25673     isApplied: function() 
25674     {
25675         return this.getGmapContext() == false ? false : true;
25676     },
25677     
25678     getGmapContext: function() 
25679     {
25680         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25681     },
25682     
25683     GMapContext: function() 
25684     {
25685         var position = new google.maps.LatLng(this.latitude, this.longitude);
25686         
25687         var _map = new google.maps.Map(this.el.dom, {
25688             center: position,
25689             zoom: this.zoom,
25690             mapTypeId: this.mapTypeId,
25691             mapTypeControl: this.mapTypeControl,
25692             disableDoubleClickZoom: this.disableDoubleClickZoom,
25693             scrollwheel: this.scrollwheel,
25694             streetViewControl: this.streetViewControl,
25695             locationName: this.locationName,
25696             draggable: this.draggable,
25697             enableAutocomplete: this.enableAutocomplete,
25698             enableReverseGeocode: this.enableReverseGeocode
25699         });
25700         
25701         var _marker = new google.maps.Marker({
25702             position: position,
25703             map: _map,
25704             title: this.markerTitle,
25705             draggable: this.draggable
25706         });
25707         
25708         return {
25709             map: _map,
25710             marker: _marker,
25711             circle: null,
25712             location: position,
25713             radius: this.radius,
25714             locationName: this.locationName,
25715             addressComponents: {
25716                 formatted_address: null,
25717                 addressLine1: null,
25718                 addressLine2: null,
25719                 streetName: null,
25720                 streetNumber: null,
25721                 city: null,
25722                 district: null,
25723                 state: null,
25724                 stateOrProvince: null
25725             },
25726             settings: this,
25727             domContainer: this.el.dom,
25728             geodecoder: new google.maps.Geocoder()
25729         };
25730     },
25731     
25732     drawCircle: function(center, radius, options) 
25733     {
25734         if (this.gMapContext.circle != null) {
25735             this.gMapContext.circle.setMap(null);
25736         }
25737         if (radius > 0) {
25738             radius *= 1;
25739             options = Roo.apply({}, options, {
25740                 strokeColor: "#0000FF",
25741                 strokeOpacity: .35,
25742                 strokeWeight: 2,
25743                 fillColor: "#0000FF",
25744                 fillOpacity: .2
25745             });
25746             
25747             options.map = this.gMapContext.map;
25748             options.radius = radius;
25749             options.center = center;
25750             this.gMapContext.circle = new google.maps.Circle(options);
25751             return this.gMapContext.circle;
25752         }
25753         
25754         return null;
25755     },
25756     
25757     setPosition: function(location) 
25758     {
25759         this.gMapContext.location = location;
25760         this.gMapContext.marker.setPosition(location);
25761         this.gMapContext.map.panTo(location);
25762         this.drawCircle(location, this.gMapContext.radius, {});
25763         
25764         var _this = this;
25765         
25766         if (this.gMapContext.settings.enableReverseGeocode) {
25767             this.gMapContext.geodecoder.geocode({
25768                 latLng: this.gMapContext.location
25769             }, function(results, status) {
25770                 
25771                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25772                     _this.gMapContext.locationName = results[0].formatted_address;
25773                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25774                     
25775                     _this.fireEvent('positionchanged', this, location);
25776                 }
25777             });
25778             
25779             return;
25780         }
25781         
25782         this.fireEvent('positionchanged', this, location);
25783     },
25784     
25785     resize: function()
25786     {
25787         google.maps.event.trigger(this.gMapContext.map, "resize");
25788         
25789         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25790         
25791         this.fireEvent('resize', this);
25792     },
25793     
25794     setPositionByLatLng: function(latitude, longitude)
25795     {
25796         this.setPosition(new google.maps.LatLng(latitude, longitude));
25797     },
25798     
25799     getCurrentPosition: function() 
25800     {
25801         return {
25802             latitude: this.gMapContext.location.lat(),
25803             longitude: this.gMapContext.location.lng()
25804         };
25805     },
25806     
25807     getAddressName: function() 
25808     {
25809         return this.gMapContext.locationName;
25810     },
25811     
25812     getAddressComponents: function() 
25813     {
25814         return this.gMapContext.addressComponents;
25815     },
25816     
25817     address_component_from_google_geocode: function(address_components) 
25818     {
25819         var result = {};
25820         
25821         for (var i = 0; i < address_components.length; i++) {
25822             var component = address_components[i];
25823             if (component.types.indexOf("postal_code") >= 0) {
25824                 result.postalCode = component.short_name;
25825             } else if (component.types.indexOf("street_number") >= 0) {
25826                 result.streetNumber = component.short_name;
25827             } else if (component.types.indexOf("route") >= 0) {
25828                 result.streetName = component.short_name;
25829             } else if (component.types.indexOf("neighborhood") >= 0) {
25830                 result.city = component.short_name;
25831             } else if (component.types.indexOf("locality") >= 0) {
25832                 result.city = component.short_name;
25833             } else if (component.types.indexOf("sublocality") >= 0) {
25834                 result.district = component.short_name;
25835             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25836                 result.stateOrProvince = component.short_name;
25837             } else if (component.types.indexOf("country") >= 0) {
25838                 result.country = component.short_name;
25839             }
25840         }
25841         
25842         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25843         result.addressLine2 = "";
25844         return result;
25845     },
25846     
25847     setZoomLevel: function(zoom)
25848     {
25849         this.gMapContext.map.setZoom(zoom);
25850     },
25851     
25852     show: function()
25853     {
25854         if(!this.el){
25855             return;
25856         }
25857         
25858         this.el.show();
25859         
25860         this.resize();
25861         
25862         this.fireEvent('show', this);
25863     },
25864     
25865     hide: function()
25866     {
25867         if(!this.el){
25868             return;
25869         }
25870         
25871         this.el.hide();
25872         
25873         this.fireEvent('hide', this);
25874     }
25875     
25876 });
25877
25878 Roo.apply(Roo.bootstrap.LocationPicker, {
25879     
25880     OverlayView : function(map, options)
25881     {
25882         options = options || {};
25883         
25884         this.setMap(map);
25885     }
25886     
25887     
25888 });/*
25889  * - LGPL
25890  *
25891  * Alert
25892  * 
25893  */
25894
25895 /**
25896  * @class Roo.bootstrap.Alert
25897  * @extends Roo.bootstrap.Component
25898  * Bootstrap Alert class
25899  * @cfg {String} title The title of alert
25900  * @cfg {String} html The content of alert
25901  * @cfg {String} weight (  success | info | warning | danger )
25902  * @cfg {String} faicon font-awesomeicon
25903  * 
25904  * @constructor
25905  * Create a new alert
25906  * @param {Object} config The config object
25907  */
25908
25909
25910 Roo.bootstrap.Alert = function(config){
25911     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25912     
25913 };
25914
25915 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25916     
25917     title: '',
25918     html: '',
25919     weight: false,
25920     faicon: false,
25921     
25922     getAutoCreate : function()
25923     {
25924         
25925         var cfg = {
25926             tag : 'div',
25927             cls : 'alert',
25928             cn : [
25929                 {
25930                     tag : 'i',
25931                     cls : 'roo-alert-icon'
25932                     
25933                 },
25934                 {
25935                     tag : 'b',
25936                     cls : 'roo-alert-title',
25937                     html : this.title
25938                 },
25939                 {
25940                     tag : 'span',
25941                     cls : 'roo-alert-text',
25942                     html : this.html
25943                 }
25944             ]
25945         };
25946         
25947         if(this.faicon){
25948             cfg.cn[0].cls += ' fa ' + this.faicon;
25949         }
25950         
25951         if(this.weight){
25952             cfg.cls += ' alert-' + this.weight;
25953         }
25954         
25955         return cfg;
25956     },
25957     
25958     initEvents: function() 
25959     {
25960         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25961     },
25962     
25963     setTitle : function(str)
25964     {
25965         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25966     },
25967     
25968     setText : function(str)
25969     {
25970         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25971     },
25972     
25973     setWeight : function(weight)
25974     {
25975         if(this.weight){
25976             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25977         }
25978         
25979         this.weight = weight;
25980         
25981         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25982     },
25983     
25984     setIcon : function(icon)
25985     {
25986         if(this.faicon){
25987             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25988         }
25989         
25990         this.faicon = icon;
25991         
25992         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25993     },
25994     
25995     hide: function() 
25996     {
25997         this.el.hide();   
25998     },
25999     
26000     show: function() 
26001     {  
26002         this.el.show();   
26003     }
26004     
26005 });
26006
26007  
26008 /*
26009 * Licence: LGPL
26010 */
26011
26012 /**
26013  * @class Roo.bootstrap.UploadCropbox
26014  * @extends Roo.bootstrap.Component
26015  * Bootstrap UploadCropbox class
26016  * @cfg {String} emptyText show when image has been loaded
26017  * @cfg {String} rotateNotify show when image too small to rotate
26018  * @cfg {Number} errorTimeout default 3000
26019  * @cfg {Number} minWidth default 300
26020  * @cfg {Number} minHeight default 300
26021  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26022  * @cfg {Boolean} isDocument (true|false) default false
26023  * @cfg {String} url action url
26024  * @cfg {String} paramName default 'imageUpload'
26025  * @cfg {String} method default POST
26026  * @cfg {Boolean} loadMask (true|false) default true
26027  * @cfg {Boolean} loadingText default 'Loading...'
26028  * 
26029  * @constructor
26030  * Create a new UploadCropbox
26031  * @param {Object} config The config object
26032  */
26033
26034 Roo.bootstrap.UploadCropbox = function(config){
26035     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26036     
26037     this.addEvents({
26038         /**
26039          * @event beforeselectfile
26040          * Fire before select file
26041          * @param {Roo.bootstrap.UploadCropbox} this
26042          */
26043         "beforeselectfile" : true,
26044         /**
26045          * @event initial
26046          * Fire after initEvent
26047          * @param {Roo.bootstrap.UploadCropbox} this
26048          */
26049         "initial" : true,
26050         /**
26051          * @event crop
26052          * Fire after initEvent
26053          * @param {Roo.bootstrap.UploadCropbox} this
26054          * @param {String} data
26055          */
26056         "crop" : true,
26057         /**
26058          * @event prepare
26059          * Fire when preparing the file data
26060          * @param {Roo.bootstrap.UploadCropbox} this
26061          * @param {Object} file
26062          */
26063         "prepare" : true,
26064         /**
26065          * @event exception
26066          * Fire when get exception
26067          * @param {Roo.bootstrap.UploadCropbox} this
26068          * @param {XMLHttpRequest} xhr
26069          */
26070         "exception" : true,
26071         /**
26072          * @event beforeloadcanvas
26073          * Fire before load the canvas
26074          * @param {Roo.bootstrap.UploadCropbox} this
26075          * @param {String} src
26076          */
26077         "beforeloadcanvas" : true,
26078         /**
26079          * @event trash
26080          * Fire when trash image
26081          * @param {Roo.bootstrap.UploadCropbox} this
26082          */
26083         "trash" : true,
26084         /**
26085          * @event download
26086          * Fire when download the image
26087          * @param {Roo.bootstrap.UploadCropbox} this
26088          */
26089         "download" : true,
26090         /**
26091          * @event footerbuttonclick
26092          * Fire when footerbuttonclick
26093          * @param {Roo.bootstrap.UploadCropbox} this
26094          * @param {String} type
26095          */
26096         "footerbuttonclick" : true,
26097         /**
26098          * @event resize
26099          * Fire when resize
26100          * @param {Roo.bootstrap.UploadCropbox} this
26101          */
26102         "resize" : true,
26103         /**
26104          * @event rotate
26105          * Fire when rotate the image
26106          * @param {Roo.bootstrap.UploadCropbox} this
26107          * @param {String} pos
26108          */
26109         "rotate" : true,
26110         /**
26111          * @event inspect
26112          * Fire when inspect the file
26113          * @param {Roo.bootstrap.UploadCropbox} this
26114          * @param {Object} file
26115          */
26116         "inspect" : true,
26117         /**
26118          * @event upload
26119          * Fire when xhr upload the file
26120          * @param {Roo.bootstrap.UploadCropbox} this
26121          * @param {Object} data
26122          */
26123         "upload" : true,
26124         /**
26125          * @event arrange
26126          * Fire when arrange the file data
26127          * @param {Roo.bootstrap.UploadCropbox} this
26128          * @param {Object} formData
26129          */
26130         "arrange" : true
26131     });
26132     
26133     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26134 };
26135
26136 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26137     
26138     emptyText : 'Click to upload image',
26139     rotateNotify : 'Image is too small to rotate',
26140     errorTimeout : 3000,
26141     scale : 0,
26142     baseScale : 1,
26143     rotate : 0,
26144     dragable : false,
26145     pinching : false,
26146     mouseX : 0,
26147     mouseY : 0,
26148     cropData : false,
26149     minWidth : 300,
26150     minHeight : 300,
26151     file : false,
26152     exif : {},
26153     baseRotate : 1,
26154     cropType : 'image/jpeg',
26155     buttons : false,
26156     canvasLoaded : false,
26157     isDocument : false,
26158     method : 'POST',
26159     paramName : 'imageUpload',
26160     loadMask : true,
26161     loadingText : 'Loading...',
26162     maskEl : false,
26163     
26164     getAutoCreate : function()
26165     {
26166         var cfg = {
26167             tag : 'div',
26168             cls : 'roo-upload-cropbox',
26169             cn : [
26170                 {
26171                     tag : 'input',
26172                     cls : 'roo-upload-cropbox-selector',
26173                     type : 'file'
26174                 },
26175                 {
26176                     tag : 'div',
26177                     cls : 'roo-upload-cropbox-body',
26178                     style : 'cursor:pointer',
26179                     cn : [
26180                         {
26181                             tag : 'div',
26182                             cls : 'roo-upload-cropbox-preview'
26183                         },
26184                         {
26185                             tag : 'div',
26186                             cls : 'roo-upload-cropbox-thumb'
26187                         },
26188                         {
26189                             tag : 'div',
26190                             cls : 'roo-upload-cropbox-empty-notify',
26191                             html : this.emptyText
26192                         },
26193                         {
26194                             tag : 'div',
26195                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26196                             html : this.rotateNotify
26197                         }
26198                     ]
26199                 },
26200                 {
26201                     tag : 'div',
26202                     cls : 'roo-upload-cropbox-footer',
26203                     cn : {
26204                         tag : 'div',
26205                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26206                         cn : []
26207                     }
26208                 }
26209             ]
26210         };
26211         
26212         return cfg;
26213     },
26214     
26215     onRender : function(ct, position)
26216     {
26217         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26218         
26219         if (this.buttons.length) {
26220             
26221             Roo.each(this.buttons, function(bb) {
26222                 
26223                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26224                 
26225                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26226                 
26227             }, this);
26228         }
26229         
26230         if(this.loadMask){
26231             this.maskEl = this.el;
26232         }
26233     },
26234     
26235     initEvents : function()
26236     {
26237         this.urlAPI = (window.createObjectURL && window) || 
26238                                 (window.URL && URL.revokeObjectURL && URL) || 
26239                                 (window.webkitURL && webkitURL);
26240                         
26241         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26242         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26243         
26244         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26245         this.selectorEl.hide();
26246         
26247         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26248         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26249         
26250         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26251         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26252         this.thumbEl.hide();
26253         
26254         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26255         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26256         
26257         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26258         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26259         this.errorEl.hide();
26260         
26261         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26262         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26263         this.footerEl.hide();
26264         
26265         this.setThumbBoxSize();
26266         
26267         this.bind();
26268         
26269         this.resize();
26270         
26271         this.fireEvent('initial', this);
26272     },
26273
26274     bind : function()
26275     {
26276         var _this = this;
26277         
26278         window.addEventListener("resize", function() { _this.resize(); } );
26279         
26280         this.bodyEl.on('click', this.beforeSelectFile, this);
26281         
26282         if(Roo.isTouch){
26283             this.bodyEl.on('touchstart', this.onTouchStart, this);
26284             this.bodyEl.on('touchmove', this.onTouchMove, this);
26285             this.bodyEl.on('touchend', this.onTouchEnd, this);
26286         }
26287         
26288         if(!Roo.isTouch){
26289             this.bodyEl.on('mousedown', this.onMouseDown, this);
26290             this.bodyEl.on('mousemove', this.onMouseMove, this);
26291             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26292             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26293             Roo.get(document).on('mouseup', this.onMouseUp, this);
26294         }
26295         
26296         this.selectorEl.on('change', this.onFileSelected, this);
26297     },
26298     
26299     reset : function()
26300     {    
26301         this.scale = 0;
26302         this.baseScale = 1;
26303         this.rotate = 0;
26304         this.baseRotate = 1;
26305         this.dragable = false;
26306         this.pinching = false;
26307         this.mouseX = 0;
26308         this.mouseY = 0;
26309         this.cropData = false;
26310         this.notifyEl.dom.innerHTML = this.emptyText;
26311         
26312         this.selectorEl.dom.value = '';
26313         
26314     },
26315     
26316     resize : function()
26317     {
26318         if(this.fireEvent('resize', this) != false){
26319             this.setThumbBoxPosition();
26320             this.setCanvasPosition();
26321         }
26322     },
26323     
26324     onFooterButtonClick : function(e, el, o, type)
26325     {
26326         switch (type) {
26327             case 'rotate-left' :
26328                 this.onRotateLeft(e);
26329                 break;
26330             case 'rotate-right' :
26331                 this.onRotateRight(e);
26332                 break;
26333             case 'picture' :
26334                 this.beforeSelectFile(e);
26335                 break;
26336             case 'trash' :
26337                 this.trash(e);
26338                 break;
26339             case 'crop' :
26340                 this.crop(e);
26341                 break;
26342             case 'download' :
26343                 this.download(e);
26344                 break;
26345             default :
26346                 break;
26347         }
26348         
26349         this.fireEvent('footerbuttonclick', this, type);
26350     },
26351     
26352     beforeSelectFile : function(e)
26353     {
26354         e.preventDefault();
26355         
26356         if(this.fireEvent('beforeselectfile', this) != false){
26357             this.selectorEl.dom.click();
26358         }
26359     },
26360     
26361     onFileSelected : function(e)
26362     {
26363         e.preventDefault();
26364         
26365         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26366             return;
26367         }
26368         
26369         var file = this.selectorEl.dom.files[0];
26370         
26371         if(this.fireEvent('inspect', this, file) != false){
26372             this.prepare(file);
26373         }
26374         
26375     },
26376     
26377     trash : function(e)
26378     {
26379         this.fireEvent('trash', this);
26380     },
26381     
26382     download : function(e)
26383     {
26384         this.fireEvent('download', this);
26385     },
26386     
26387     loadCanvas : function(src)
26388     {   
26389         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26390             
26391             this.reset();
26392             
26393             this.imageEl = document.createElement('img');
26394             
26395             var _this = this;
26396             
26397             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26398             
26399             this.imageEl.src = src;
26400         }
26401     },
26402     
26403     onLoadCanvas : function()
26404     {   
26405         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26406         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26407         
26408         this.bodyEl.un('click', this.beforeSelectFile, this);
26409         
26410         this.notifyEl.hide();
26411         this.thumbEl.show();
26412         this.footerEl.show();
26413         
26414         this.baseRotateLevel();
26415         
26416         if(this.isDocument){
26417             this.setThumbBoxSize();
26418         }
26419         
26420         this.setThumbBoxPosition();
26421         
26422         this.baseScaleLevel();
26423         
26424         this.draw();
26425         
26426         this.resize();
26427         
26428         this.canvasLoaded = true;
26429         
26430         if(this.loadMask){
26431             this.maskEl.unmask();
26432         }
26433         
26434     },
26435     
26436     setCanvasPosition : function()
26437     {   
26438         if(!this.canvasEl){
26439             return;
26440         }
26441         
26442         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26443         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26444         
26445         this.previewEl.setLeft(pw);
26446         this.previewEl.setTop(ph);
26447         
26448     },
26449     
26450     onMouseDown : function(e)
26451     {   
26452         e.stopEvent();
26453         
26454         this.dragable = true;
26455         this.pinching = false;
26456         
26457         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26458             this.dragable = false;
26459             return;
26460         }
26461         
26462         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26463         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26464         
26465     },
26466     
26467     onMouseMove : function(e)
26468     {   
26469         e.stopEvent();
26470         
26471         if(!this.canvasLoaded){
26472             return;
26473         }
26474         
26475         if (!this.dragable){
26476             return;
26477         }
26478         
26479         var minX = Math.ceil(this.thumbEl.getLeft(true));
26480         var minY = Math.ceil(this.thumbEl.getTop(true));
26481         
26482         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26483         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26484         
26485         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26486         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26487         
26488         x = x - this.mouseX;
26489         y = y - this.mouseY;
26490         
26491         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26492         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26493         
26494         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26495         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26496         
26497         this.previewEl.setLeft(bgX);
26498         this.previewEl.setTop(bgY);
26499         
26500         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26501         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26502     },
26503     
26504     onMouseUp : function(e)
26505     {   
26506         e.stopEvent();
26507         
26508         this.dragable = false;
26509     },
26510     
26511     onMouseWheel : function(e)
26512     {   
26513         e.stopEvent();
26514         
26515         this.startScale = this.scale;
26516         
26517         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26518         
26519         if(!this.zoomable()){
26520             this.scale = this.startScale;
26521             return;
26522         }
26523         
26524         this.draw();
26525         
26526         return;
26527     },
26528     
26529     zoomable : function()
26530     {
26531         var minScale = this.thumbEl.getWidth() / this.minWidth;
26532         
26533         if(this.minWidth < this.minHeight){
26534             minScale = this.thumbEl.getHeight() / this.minHeight;
26535         }
26536         
26537         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26538         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26539         
26540         if(
26541                 this.isDocument &&
26542                 (this.rotate == 0 || this.rotate == 180) && 
26543                 (
26544                     width > this.imageEl.OriginWidth || 
26545                     height > this.imageEl.OriginHeight ||
26546                     (width < this.minWidth && height < this.minHeight)
26547                 )
26548         ){
26549             return false;
26550         }
26551         
26552         if(
26553                 this.isDocument &&
26554                 (this.rotate == 90 || this.rotate == 270) && 
26555                 (
26556                     width > this.imageEl.OriginWidth || 
26557                     height > this.imageEl.OriginHeight ||
26558                     (width < this.minHeight && height < this.minWidth)
26559                 )
26560         ){
26561             return false;
26562         }
26563         
26564         if(
26565                 !this.isDocument &&
26566                 (this.rotate == 0 || this.rotate == 180) && 
26567                 (
26568                     width < this.minWidth || 
26569                     width > this.imageEl.OriginWidth || 
26570                     height < this.minHeight || 
26571                     height > this.imageEl.OriginHeight
26572                 )
26573         ){
26574             return false;
26575         }
26576         
26577         if(
26578                 !this.isDocument &&
26579                 (this.rotate == 90 || this.rotate == 270) && 
26580                 (
26581                     width < this.minHeight || 
26582                     width > this.imageEl.OriginWidth || 
26583                     height < this.minWidth || 
26584                     height > this.imageEl.OriginHeight
26585                 )
26586         ){
26587             return false;
26588         }
26589         
26590         return true;
26591         
26592     },
26593     
26594     onRotateLeft : function(e)
26595     {   
26596         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26597             
26598             var minScale = this.thumbEl.getWidth() / this.minWidth;
26599             
26600             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26601             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26602             
26603             this.startScale = this.scale;
26604             
26605             while (this.getScaleLevel() < minScale){
26606             
26607                 this.scale = this.scale + 1;
26608                 
26609                 if(!this.zoomable()){
26610                     break;
26611                 }
26612                 
26613                 if(
26614                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26615                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26616                 ){
26617                     continue;
26618                 }
26619                 
26620                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26621
26622                 this.draw();
26623                 
26624                 return;
26625             }
26626             
26627             this.scale = this.startScale;
26628             
26629             this.onRotateFail();
26630             
26631             return false;
26632         }
26633         
26634         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26635
26636         if(this.isDocument){
26637             this.setThumbBoxSize();
26638             this.setThumbBoxPosition();
26639             this.setCanvasPosition();
26640         }
26641         
26642         this.draw();
26643         
26644         this.fireEvent('rotate', this, 'left');
26645         
26646     },
26647     
26648     onRotateRight : function(e)
26649     {
26650         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26651             
26652             var minScale = this.thumbEl.getWidth() / this.minWidth;
26653         
26654             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26655             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26656             
26657             this.startScale = this.scale;
26658             
26659             while (this.getScaleLevel() < minScale){
26660             
26661                 this.scale = this.scale + 1;
26662                 
26663                 if(!this.zoomable()){
26664                     break;
26665                 }
26666                 
26667                 if(
26668                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26669                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26670                 ){
26671                     continue;
26672                 }
26673                 
26674                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26675
26676                 this.draw();
26677                 
26678                 return;
26679             }
26680             
26681             this.scale = this.startScale;
26682             
26683             this.onRotateFail();
26684             
26685             return false;
26686         }
26687         
26688         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26689
26690         if(this.isDocument){
26691             this.setThumbBoxSize();
26692             this.setThumbBoxPosition();
26693             this.setCanvasPosition();
26694         }
26695         
26696         this.draw();
26697         
26698         this.fireEvent('rotate', this, 'right');
26699     },
26700     
26701     onRotateFail : function()
26702     {
26703         this.errorEl.show(true);
26704         
26705         var _this = this;
26706         
26707         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26708     },
26709     
26710     draw : function()
26711     {
26712         this.previewEl.dom.innerHTML = '';
26713         
26714         var canvasEl = document.createElement("canvas");
26715         
26716         var contextEl = canvasEl.getContext("2d");
26717         
26718         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26719         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26720         var center = this.imageEl.OriginWidth / 2;
26721         
26722         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26723             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26724             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26725             center = this.imageEl.OriginHeight / 2;
26726         }
26727         
26728         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26729         
26730         contextEl.translate(center, center);
26731         contextEl.rotate(this.rotate * Math.PI / 180);
26732
26733         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26734         
26735         this.canvasEl = document.createElement("canvas");
26736         
26737         this.contextEl = this.canvasEl.getContext("2d");
26738         
26739         switch (this.rotate) {
26740             case 0 :
26741                 
26742                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26743                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26744                 
26745                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26746                 
26747                 break;
26748             case 90 : 
26749                 
26750                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26751                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26752                 
26753                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26754                     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);
26755                     break;
26756                 }
26757                 
26758                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26759                 
26760                 break;
26761             case 180 :
26762                 
26763                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26764                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26765                 
26766                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26767                     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);
26768                     break;
26769                 }
26770                 
26771                 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);
26772                 
26773                 break;
26774             case 270 :
26775                 
26776                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26777                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26778         
26779                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26780                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26781                     break;
26782                 }
26783                 
26784                 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);
26785                 
26786                 break;
26787             default : 
26788                 break;
26789         }
26790         
26791         this.previewEl.appendChild(this.canvasEl);
26792         
26793         this.setCanvasPosition();
26794     },
26795     
26796     crop : function()
26797     {
26798         if(!this.canvasLoaded){
26799             return;
26800         }
26801         
26802         var imageCanvas = document.createElement("canvas");
26803         
26804         var imageContext = imageCanvas.getContext("2d");
26805         
26806         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26807         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26808         
26809         var center = imageCanvas.width / 2;
26810         
26811         imageContext.translate(center, center);
26812         
26813         imageContext.rotate(this.rotate * Math.PI / 180);
26814         
26815         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26816         
26817         var canvas = document.createElement("canvas");
26818         
26819         var context = canvas.getContext("2d");
26820                 
26821         canvas.width = this.minWidth;
26822         canvas.height = this.minHeight;
26823
26824         switch (this.rotate) {
26825             case 0 :
26826                 
26827                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26828                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26829                 
26830                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26831                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26832                 
26833                 var targetWidth = this.minWidth - 2 * x;
26834                 var targetHeight = this.minHeight - 2 * y;
26835                 
26836                 var scale = 1;
26837                 
26838                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26839                     scale = targetWidth / width;
26840                 }
26841                 
26842                 if(x > 0 && y == 0){
26843                     scale = targetHeight / height;
26844                 }
26845                 
26846                 if(x > 0 && y > 0){
26847                     scale = targetWidth / width;
26848                     
26849                     if(width < height){
26850                         scale = targetHeight / height;
26851                     }
26852                 }
26853                 
26854                 context.scale(scale, scale);
26855                 
26856                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26857                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26858
26859                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26860                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26861
26862                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26863                 
26864                 break;
26865             case 90 : 
26866                 
26867                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26868                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26869                 
26870                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26871                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26872                 
26873                 var targetWidth = this.minWidth - 2 * x;
26874                 var targetHeight = this.minHeight - 2 * y;
26875                 
26876                 var scale = 1;
26877                 
26878                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26879                     scale = targetWidth / width;
26880                 }
26881                 
26882                 if(x > 0 && y == 0){
26883                     scale = targetHeight / height;
26884                 }
26885                 
26886                 if(x > 0 && y > 0){
26887                     scale = targetWidth / width;
26888                     
26889                     if(width < height){
26890                         scale = targetHeight / height;
26891                     }
26892                 }
26893                 
26894                 context.scale(scale, scale);
26895                 
26896                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26897                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26898
26899                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26900                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26901                 
26902                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26903                 
26904                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26905                 
26906                 break;
26907             case 180 :
26908                 
26909                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26910                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26911                 
26912                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26913                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26914                 
26915                 var targetWidth = this.minWidth - 2 * x;
26916                 var targetHeight = this.minHeight - 2 * y;
26917                 
26918                 var scale = 1;
26919                 
26920                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26921                     scale = targetWidth / width;
26922                 }
26923                 
26924                 if(x > 0 && y == 0){
26925                     scale = targetHeight / height;
26926                 }
26927                 
26928                 if(x > 0 && y > 0){
26929                     scale = targetWidth / width;
26930                     
26931                     if(width < height){
26932                         scale = targetHeight / height;
26933                     }
26934                 }
26935                 
26936                 context.scale(scale, scale);
26937                 
26938                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26939                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26940
26941                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26942                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26943
26944                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26945                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26946                 
26947                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26948                 
26949                 break;
26950             case 270 :
26951                 
26952                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26953                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26954                 
26955                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26956                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26957                 
26958                 var targetWidth = this.minWidth - 2 * x;
26959                 var targetHeight = this.minHeight - 2 * y;
26960                 
26961                 var scale = 1;
26962                 
26963                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26964                     scale = targetWidth / width;
26965                 }
26966                 
26967                 if(x > 0 && y == 0){
26968                     scale = targetHeight / height;
26969                 }
26970                 
26971                 if(x > 0 && y > 0){
26972                     scale = targetWidth / width;
26973                     
26974                     if(width < height){
26975                         scale = targetHeight / height;
26976                     }
26977                 }
26978                 
26979                 context.scale(scale, scale);
26980                 
26981                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26982                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26983
26984                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26985                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26986                 
26987                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26988                 
26989                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26990                 
26991                 break;
26992             default : 
26993                 break;
26994         }
26995         
26996         this.cropData = canvas.toDataURL(this.cropType);
26997         
26998         if(this.fireEvent('crop', this, this.cropData) !== false){
26999             this.process(this.file, this.cropData);
27000         }
27001         
27002         return;
27003         
27004     },
27005     
27006     setThumbBoxSize : function()
27007     {
27008         var width, height;
27009         
27010         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27011             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27012             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27013             
27014             this.minWidth = width;
27015             this.minHeight = height;
27016             
27017             if(this.rotate == 90 || this.rotate == 270){
27018                 this.minWidth = height;
27019                 this.minHeight = width;
27020             }
27021         }
27022         
27023         height = 300;
27024         width = Math.ceil(this.minWidth * height / this.minHeight);
27025         
27026         if(this.minWidth > this.minHeight){
27027             width = 300;
27028             height = Math.ceil(this.minHeight * width / this.minWidth);
27029         }
27030         
27031         this.thumbEl.setStyle({
27032             width : width + 'px',
27033             height : height + 'px'
27034         });
27035
27036         return;
27037             
27038     },
27039     
27040     setThumbBoxPosition : function()
27041     {
27042         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27043         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27044         
27045         this.thumbEl.setLeft(x);
27046         this.thumbEl.setTop(y);
27047         
27048     },
27049     
27050     baseRotateLevel : function()
27051     {
27052         this.baseRotate = 1;
27053         
27054         if(
27055                 typeof(this.exif) != 'undefined' &&
27056                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27057                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27058         ){
27059             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27060         }
27061         
27062         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27063         
27064     },
27065     
27066     baseScaleLevel : function()
27067     {
27068         var width, height;
27069         
27070         if(this.isDocument){
27071             
27072             if(this.baseRotate == 6 || this.baseRotate == 8){
27073             
27074                 height = this.thumbEl.getHeight();
27075                 this.baseScale = height / this.imageEl.OriginWidth;
27076
27077                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27078                     width = this.thumbEl.getWidth();
27079                     this.baseScale = width / this.imageEl.OriginHeight;
27080                 }
27081
27082                 return;
27083             }
27084
27085             height = this.thumbEl.getHeight();
27086             this.baseScale = height / this.imageEl.OriginHeight;
27087
27088             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27089                 width = this.thumbEl.getWidth();
27090                 this.baseScale = width / this.imageEl.OriginWidth;
27091             }
27092
27093             return;
27094         }
27095         
27096         if(this.baseRotate == 6 || this.baseRotate == 8){
27097             
27098             width = this.thumbEl.getHeight();
27099             this.baseScale = width / this.imageEl.OriginHeight;
27100             
27101             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27102                 height = this.thumbEl.getWidth();
27103                 this.baseScale = height / this.imageEl.OriginHeight;
27104             }
27105             
27106             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27107                 height = this.thumbEl.getWidth();
27108                 this.baseScale = height / this.imageEl.OriginHeight;
27109                 
27110                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27111                     width = this.thumbEl.getHeight();
27112                     this.baseScale = width / this.imageEl.OriginWidth;
27113                 }
27114             }
27115             
27116             return;
27117         }
27118         
27119         width = this.thumbEl.getWidth();
27120         this.baseScale = width / this.imageEl.OriginWidth;
27121         
27122         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27123             height = this.thumbEl.getHeight();
27124             this.baseScale = height / this.imageEl.OriginHeight;
27125         }
27126         
27127         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27128             
27129             height = this.thumbEl.getHeight();
27130             this.baseScale = height / this.imageEl.OriginHeight;
27131             
27132             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27133                 width = this.thumbEl.getWidth();
27134                 this.baseScale = width / this.imageEl.OriginWidth;
27135             }
27136             
27137         }
27138         
27139         return;
27140     },
27141     
27142     getScaleLevel : function()
27143     {
27144         return this.baseScale * Math.pow(1.1, this.scale);
27145     },
27146     
27147     onTouchStart : function(e)
27148     {
27149         if(!this.canvasLoaded){
27150             this.beforeSelectFile(e);
27151             return;
27152         }
27153         
27154         var touches = e.browserEvent.touches;
27155         
27156         if(!touches){
27157             return;
27158         }
27159         
27160         if(touches.length == 1){
27161             this.onMouseDown(e);
27162             return;
27163         }
27164         
27165         if(touches.length != 2){
27166             return;
27167         }
27168         
27169         var coords = [];
27170         
27171         for(var i = 0, finger; finger = touches[i]; i++){
27172             coords.push(finger.pageX, finger.pageY);
27173         }
27174         
27175         var x = Math.pow(coords[0] - coords[2], 2);
27176         var y = Math.pow(coords[1] - coords[3], 2);
27177         
27178         this.startDistance = Math.sqrt(x + y);
27179         
27180         this.startScale = this.scale;
27181         
27182         this.pinching = true;
27183         this.dragable = false;
27184         
27185     },
27186     
27187     onTouchMove : function(e)
27188     {
27189         if(!this.pinching && !this.dragable){
27190             return;
27191         }
27192         
27193         var touches = e.browserEvent.touches;
27194         
27195         if(!touches){
27196             return;
27197         }
27198         
27199         if(this.dragable){
27200             this.onMouseMove(e);
27201             return;
27202         }
27203         
27204         var coords = [];
27205         
27206         for(var i = 0, finger; finger = touches[i]; i++){
27207             coords.push(finger.pageX, finger.pageY);
27208         }
27209         
27210         var x = Math.pow(coords[0] - coords[2], 2);
27211         var y = Math.pow(coords[1] - coords[3], 2);
27212         
27213         this.endDistance = Math.sqrt(x + y);
27214         
27215         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27216         
27217         if(!this.zoomable()){
27218             this.scale = this.startScale;
27219             return;
27220         }
27221         
27222         this.draw();
27223         
27224     },
27225     
27226     onTouchEnd : function(e)
27227     {
27228         this.pinching = false;
27229         this.dragable = false;
27230         
27231     },
27232     
27233     process : function(file, crop)
27234     {
27235         if(this.loadMask){
27236             this.maskEl.mask(this.loadingText);
27237         }
27238         
27239         this.xhr = new XMLHttpRequest();
27240         
27241         file.xhr = this.xhr;
27242
27243         this.xhr.open(this.method, this.url, true);
27244         
27245         var headers = {
27246             "Accept": "application/json",
27247             "Cache-Control": "no-cache",
27248             "X-Requested-With": "XMLHttpRequest"
27249         };
27250         
27251         for (var headerName in headers) {
27252             var headerValue = headers[headerName];
27253             if (headerValue) {
27254                 this.xhr.setRequestHeader(headerName, headerValue);
27255             }
27256         }
27257         
27258         var _this = this;
27259         
27260         this.xhr.onload = function()
27261         {
27262             _this.xhrOnLoad(_this.xhr);
27263         }
27264         
27265         this.xhr.onerror = function()
27266         {
27267             _this.xhrOnError(_this.xhr);
27268         }
27269         
27270         var formData = new FormData();
27271
27272         formData.append('returnHTML', 'NO');
27273         
27274         if(crop){
27275             formData.append('crop', crop);
27276         }
27277         
27278         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27279             formData.append(this.paramName, file, file.name);
27280         }
27281         
27282         if(typeof(file.filename) != 'undefined'){
27283             formData.append('filename', file.filename);
27284         }
27285         
27286         if(typeof(file.mimetype) != 'undefined'){
27287             formData.append('mimetype', file.mimetype);
27288         }
27289         
27290         if(this.fireEvent('arrange', this, formData) != false){
27291             this.xhr.send(formData);
27292         };
27293     },
27294     
27295     xhrOnLoad : function(xhr)
27296     {
27297         if(this.loadMask){
27298             this.maskEl.unmask();
27299         }
27300         
27301         if (xhr.readyState !== 4) {
27302             this.fireEvent('exception', this, xhr);
27303             return;
27304         }
27305
27306         var response = Roo.decode(xhr.responseText);
27307         
27308         if(!response.success){
27309             this.fireEvent('exception', this, xhr);
27310             return;
27311         }
27312         
27313         var response = Roo.decode(xhr.responseText);
27314         
27315         this.fireEvent('upload', this, response);
27316         
27317     },
27318     
27319     xhrOnError : function()
27320     {
27321         if(this.loadMask){
27322             this.maskEl.unmask();
27323         }
27324         
27325         Roo.log('xhr on error');
27326         
27327         var response = Roo.decode(xhr.responseText);
27328           
27329         Roo.log(response);
27330         
27331     },
27332     
27333     prepare : function(file)
27334     {   
27335         if(this.loadMask){
27336             this.maskEl.mask(this.loadingText);
27337         }
27338         
27339         this.file = false;
27340         this.exif = {};
27341         
27342         if(typeof(file) === 'string'){
27343             this.loadCanvas(file);
27344             return;
27345         }
27346         
27347         if(!file || !this.urlAPI){
27348             return;
27349         }
27350         
27351         this.file = file;
27352         this.cropType = file.type;
27353         
27354         var _this = this;
27355         
27356         if(this.fireEvent('prepare', this, this.file) != false){
27357             
27358             var reader = new FileReader();
27359             
27360             reader.onload = function (e) {
27361                 if (e.target.error) {
27362                     Roo.log(e.target.error);
27363                     return;
27364                 }
27365                 
27366                 var buffer = e.target.result,
27367                     dataView = new DataView(buffer),
27368                     offset = 2,
27369                     maxOffset = dataView.byteLength - 4,
27370                     markerBytes,
27371                     markerLength;
27372                 
27373                 if (dataView.getUint16(0) === 0xffd8) {
27374                     while (offset < maxOffset) {
27375                         markerBytes = dataView.getUint16(offset);
27376                         
27377                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27378                             markerLength = dataView.getUint16(offset + 2) + 2;
27379                             if (offset + markerLength > dataView.byteLength) {
27380                                 Roo.log('Invalid meta data: Invalid segment size.');
27381                                 break;
27382                             }
27383                             
27384                             if(markerBytes == 0xffe1){
27385                                 _this.parseExifData(
27386                                     dataView,
27387                                     offset,
27388                                     markerLength
27389                                 );
27390                             }
27391                             
27392                             offset += markerLength;
27393                             
27394                             continue;
27395                         }
27396                         
27397                         break;
27398                     }
27399                     
27400                 }
27401                 
27402                 var url = _this.urlAPI.createObjectURL(_this.file);
27403                 
27404                 _this.loadCanvas(url);
27405                 
27406                 return;
27407             }
27408             
27409             reader.readAsArrayBuffer(this.file);
27410             
27411         }
27412         
27413     },
27414     
27415     parseExifData : function(dataView, offset, length)
27416     {
27417         var tiffOffset = offset + 10,
27418             littleEndian,
27419             dirOffset;
27420     
27421         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27422             // No Exif data, might be XMP data instead
27423             return;
27424         }
27425         
27426         // Check for the ASCII code for "Exif" (0x45786966):
27427         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27428             // No Exif data, might be XMP data instead
27429             return;
27430         }
27431         if (tiffOffset + 8 > dataView.byteLength) {
27432             Roo.log('Invalid Exif data: Invalid segment size.');
27433             return;
27434         }
27435         // Check for the two null bytes:
27436         if (dataView.getUint16(offset + 8) !== 0x0000) {
27437             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27438             return;
27439         }
27440         // Check the byte alignment:
27441         switch (dataView.getUint16(tiffOffset)) {
27442         case 0x4949:
27443             littleEndian = true;
27444             break;
27445         case 0x4D4D:
27446             littleEndian = false;
27447             break;
27448         default:
27449             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27450             return;
27451         }
27452         // Check for the TIFF tag marker (0x002A):
27453         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27454             Roo.log('Invalid Exif data: Missing TIFF marker.');
27455             return;
27456         }
27457         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27458         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27459         
27460         this.parseExifTags(
27461             dataView,
27462             tiffOffset,
27463             tiffOffset + dirOffset,
27464             littleEndian
27465         );
27466     },
27467     
27468     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27469     {
27470         var tagsNumber,
27471             dirEndOffset,
27472             i;
27473         if (dirOffset + 6 > dataView.byteLength) {
27474             Roo.log('Invalid Exif data: Invalid directory offset.');
27475             return;
27476         }
27477         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27478         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27479         if (dirEndOffset + 4 > dataView.byteLength) {
27480             Roo.log('Invalid Exif data: Invalid directory size.');
27481             return;
27482         }
27483         for (i = 0; i < tagsNumber; i += 1) {
27484             this.parseExifTag(
27485                 dataView,
27486                 tiffOffset,
27487                 dirOffset + 2 + 12 * i, // tag offset
27488                 littleEndian
27489             );
27490         }
27491         // Return the offset to the next directory:
27492         return dataView.getUint32(dirEndOffset, littleEndian);
27493     },
27494     
27495     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27496     {
27497         var tag = dataView.getUint16(offset, littleEndian);
27498         
27499         this.exif[tag] = this.getExifValue(
27500             dataView,
27501             tiffOffset,
27502             offset,
27503             dataView.getUint16(offset + 2, littleEndian), // tag type
27504             dataView.getUint32(offset + 4, littleEndian), // tag length
27505             littleEndian
27506         );
27507     },
27508     
27509     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27510     {
27511         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27512             tagSize,
27513             dataOffset,
27514             values,
27515             i,
27516             str,
27517             c;
27518     
27519         if (!tagType) {
27520             Roo.log('Invalid Exif data: Invalid tag type.');
27521             return;
27522         }
27523         
27524         tagSize = tagType.size * length;
27525         // Determine if the value is contained in the dataOffset bytes,
27526         // or if the value at the dataOffset is a pointer to the actual data:
27527         dataOffset = tagSize > 4 ?
27528                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27529         if (dataOffset + tagSize > dataView.byteLength) {
27530             Roo.log('Invalid Exif data: Invalid data offset.');
27531             return;
27532         }
27533         if (length === 1) {
27534             return tagType.getValue(dataView, dataOffset, littleEndian);
27535         }
27536         values = [];
27537         for (i = 0; i < length; i += 1) {
27538             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27539         }
27540         
27541         if (tagType.ascii) {
27542             str = '';
27543             // Concatenate the chars:
27544             for (i = 0; i < values.length; i += 1) {
27545                 c = values[i];
27546                 // Ignore the terminating NULL byte(s):
27547                 if (c === '\u0000') {
27548                     break;
27549                 }
27550                 str += c;
27551             }
27552             return str;
27553         }
27554         return values;
27555     }
27556     
27557 });
27558
27559 Roo.apply(Roo.bootstrap.UploadCropbox, {
27560     tags : {
27561         'Orientation': 0x0112
27562     },
27563     
27564     Orientation: {
27565             1: 0, //'top-left',
27566 //            2: 'top-right',
27567             3: 180, //'bottom-right',
27568 //            4: 'bottom-left',
27569 //            5: 'left-top',
27570             6: 90, //'right-top',
27571 //            7: 'right-bottom',
27572             8: 270 //'left-bottom'
27573     },
27574     
27575     exifTagTypes : {
27576         // byte, 8-bit unsigned int:
27577         1: {
27578             getValue: function (dataView, dataOffset) {
27579                 return dataView.getUint8(dataOffset);
27580             },
27581             size: 1
27582         },
27583         // ascii, 8-bit byte:
27584         2: {
27585             getValue: function (dataView, dataOffset) {
27586                 return String.fromCharCode(dataView.getUint8(dataOffset));
27587             },
27588             size: 1,
27589             ascii: true
27590         },
27591         // short, 16 bit int:
27592         3: {
27593             getValue: function (dataView, dataOffset, littleEndian) {
27594                 return dataView.getUint16(dataOffset, littleEndian);
27595             },
27596             size: 2
27597         },
27598         // long, 32 bit int:
27599         4: {
27600             getValue: function (dataView, dataOffset, littleEndian) {
27601                 return dataView.getUint32(dataOffset, littleEndian);
27602             },
27603             size: 4
27604         },
27605         // rational = two long values, first is numerator, second is denominator:
27606         5: {
27607             getValue: function (dataView, dataOffset, littleEndian) {
27608                 return dataView.getUint32(dataOffset, littleEndian) /
27609                     dataView.getUint32(dataOffset + 4, littleEndian);
27610             },
27611             size: 8
27612         },
27613         // slong, 32 bit signed int:
27614         9: {
27615             getValue: function (dataView, dataOffset, littleEndian) {
27616                 return dataView.getInt32(dataOffset, littleEndian);
27617             },
27618             size: 4
27619         },
27620         // srational, two slongs, first is numerator, second is denominator:
27621         10: {
27622             getValue: function (dataView, dataOffset, littleEndian) {
27623                 return dataView.getInt32(dataOffset, littleEndian) /
27624                     dataView.getInt32(dataOffset + 4, littleEndian);
27625             },
27626             size: 8
27627         }
27628     },
27629     
27630     footer : {
27631         STANDARD : [
27632             {
27633                 tag : 'div',
27634                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27635                 action : 'rotate-left',
27636                 cn : [
27637                     {
27638                         tag : 'button',
27639                         cls : 'btn btn-default',
27640                         html : '<i class="fa fa-undo"></i>'
27641                     }
27642                 ]
27643             },
27644             {
27645                 tag : 'div',
27646                 cls : 'btn-group roo-upload-cropbox-picture',
27647                 action : 'picture',
27648                 cn : [
27649                     {
27650                         tag : 'button',
27651                         cls : 'btn btn-default',
27652                         html : '<i class="fa fa-picture-o"></i>'
27653                     }
27654                 ]
27655             },
27656             {
27657                 tag : 'div',
27658                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27659                 action : 'rotate-right',
27660                 cn : [
27661                     {
27662                         tag : 'button',
27663                         cls : 'btn btn-default',
27664                         html : '<i class="fa fa-repeat"></i>'
27665                     }
27666                 ]
27667             }
27668         ],
27669         DOCUMENT : [
27670             {
27671                 tag : 'div',
27672                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27673                 action : 'rotate-left',
27674                 cn : [
27675                     {
27676                         tag : 'button',
27677                         cls : 'btn btn-default',
27678                         html : '<i class="fa fa-undo"></i>'
27679                     }
27680                 ]
27681             },
27682             {
27683                 tag : 'div',
27684                 cls : 'btn-group roo-upload-cropbox-download',
27685                 action : 'download',
27686                 cn : [
27687                     {
27688                         tag : 'button',
27689                         cls : 'btn btn-default',
27690                         html : '<i class="fa fa-download"></i>'
27691                     }
27692                 ]
27693             },
27694             {
27695                 tag : 'div',
27696                 cls : 'btn-group roo-upload-cropbox-crop',
27697                 action : 'crop',
27698                 cn : [
27699                     {
27700                         tag : 'button',
27701                         cls : 'btn btn-default',
27702                         html : '<i class="fa fa-crop"></i>'
27703                     }
27704                 ]
27705             },
27706             {
27707                 tag : 'div',
27708                 cls : 'btn-group roo-upload-cropbox-trash',
27709                 action : 'trash',
27710                 cn : [
27711                     {
27712                         tag : 'button',
27713                         cls : 'btn btn-default',
27714                         html : '<i class="fa fa-trash"></i>'
27715                     }
27716                 ]
27717             },
27718             {
27719                 tag : 'div',
27720                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27721                 action : 'rotate-right',
27722                 cn : [
27723                     {
27724                         tag : 'button',
27725                         cls : 'btn btn-default',
27726                         html : '<i class="fa fa-repeat"></i>'
27727                     }
27728                 ]
27729             }
27730         ],
27731         ROTATOR : [
27732             {
27733                 tag : 'div',
27734                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27735                 action : 'rotate-left',
27736                 cn : [
27737                     {
27738                         tag : 'button',
27739                         cls : 'btn btn-default',
27740                         html : '<i class="fa fa-undo"></i>'
27741                     }
27742                 ]
27743             },
27744             {
27745                 tag : 'div',
27746                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27747                 action : 'rotate-right',
27748                 cn : [
27749                     {
27750                         tag : 'button',
27751                         cls : 'btn btn-default',
27752                         html : '<i class="fa fa-repeat"></i>'
27753                     }
27754                 ]
27755             }
27756         ]
27757     }
27758 });
27759
27760 /*
27761 * Licence: LGPL
27762 */
27763
27764 /**
27765  * @class Roo.bootstrap.DocumentManager
27766  * @extends Roo.bootstrap.Component
27767  * Bootstrap DocumentManager class
27768  * @cfg {String} paramName default 'imageUpload'
27769  * @cfg {String} toolTipName default 'filename'
27770  * @cfg {String} method default POST
27771  * @cfg {String} url action url
27772  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27773  * @cfg {Boolean} multiple multiple upload default true
27774  * @cfg {Number} thumbSize default 300
27775  * @cfg {String} fieldLabel
27776  * @cfg {Number} labelWidth default 4
27777  * @cfg {String} labelAlign (left|top) default left
27778  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27779 * @cfg {Number} labellg set the width of label (1-12)
27780  * @cfg {Number} labelmd set the width of label (1-12)
27781  * @cfg {Number} labelsm set the width of label (1-12)
27782  * @cfg {Number} labelxs set the width of label (1-12)
27783  * 
27784  * @constructor
27785  * Create a new DocumentManager
27786  * @param {Object} config The config object
27787  */
27788
27789 Roo.bootstrap.DocumentManager = function(config){
27790     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27791     
27792     this.files = [];
27793     this.delegates = [];
27794     
27795     this.addEvents({
27796         /**
27797          * @event initial
27798          * Fire when initial the DocumentManager
27799          * @param {Roo.bootstrap.DocumentManager} this
27800          */
27801         "initial" : true,
27802         /**
27803          * @event inspect
27804          * inspect selected file
27805          * @param {Roo.bootstrap.DocumentManager} this
27806          * @param {File} file
27807          */
27808         "inspect" : true,
27809         /**
27810          * @event exception
27811          * Fire when xhr load exception
27812          * @param {Roo.bootstrap.DocumentManager} this
27813          * @param {XMLHttpRequest} xhr
27814          */
27815         "exception" : true,
27816         /**
27817          * @event afterupload
27818          * Fire when xhr load exception
27819          * @param {Roo.bootstrap.DocumentManager} this
27820          * @param {XMLHttpRequest} xhr
27821          */
27822         "afterupload" : true,
27823         /**
27824          * @event prepare
27825          * prepare the form data
27826          * @param {Roo.bootstrap.DocumentManager} this
27827          * @param {Object} formData
27828          */
27829         "prepare" : true,
27830         /**
27831          * @event remove
27832          * Fire when remove the file
27833          * @param {Roo.bootstrap.DocumentManager} this
27834          * @param {Object} file
27835          */
27836         "remove" : true,
27837         /**
27838          * @event refresh
27839          * Fire after refresh the file
27840          * @param {Roo.bootstrap.DocumentManager} this
27841          */
27842         "refresh" : true,
27843         /**
27844          * @event click
27845          * Fire after click the image
27846          * @param {Roo.bootstrap.DocumentManager} this
27847          * @param {Object} file
27848          */
27849         "click" : true,
27850         /**
27851          * @event edit
27852          * Fire when upload a image and editable set to true
27853          * @param {Roo.bootstrap.DocumentManager} this
27854          * @param {Object} file
27855          */
27856         "edit" : true,
27857         /**
27858          * @event beforeselectfile
27859          * Fire before select file
27860          * @param {Roo.bootstrap.DocumentManager} this
27861          */
27862         "beforeselectfile" : true,
27863         /**
27864          * @event process
27865          * Fire before process file
27866          * @param {Roo.bootstrap.DocumentManager} this
27867          * @param {Object} file
27868          */
27869         "process" : true
27870         
27871     });
27872 };
27873
27874 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27875     
27876     boxes : 0,
27877     inputName : '',
27878     thumbSize : 300,
27879     multiple : true,
27880     files : false,
27881     method : 'POST',
27882     url : '',
27883     paramName : 'imageUpload',
27884     toolTipName : 'filename',
27885     fieldLabel : '',
27886     labelWidth : 4,
27887     labelAlign : 'left',
27888     editable : true,
27889     delegates : false,
27890     xhr : false, 
27891     
27892     labellg : 0,
27893     labelmd : 0,
27894     labelsm : 0,
27895     labelxs : 0,
27896     
27897     getAutoCreate : function()
27898     {   
27899         var managerWidget = {
27900             tag : 'div',
27901             cls : 'roo-document-manager',
27902             cn : [
27903                 {
27904                     tag : 'input',
27905                     cls : 'roo-document-manager-selector',
27906                     type : 'file'
27907                 },
27908                 {
27909                     tag : 'div',
27910                     cls : 'roo-document-manager-uploader',
27911                     cn : [
27912                         {
27913                             tag : 'div',
27914                             cls : 'roo-document-manager-upload-btn',
27915                             html : '<i class="fa fa-plus"></i>'
27916                         }
27917                     ]
27918                     
27919                 }
27920             ]
27921         };
27922         
27923         var content = [
27924             {
27925                 tag : 'div',
27926                 cls : 'column col-md-12',
27927                 cn : managerWidget
27928             }
27929         ];
27930         
27931         if(this.fieldLabel.length){
27932             
27933             content = [
27934                 {
27935                     tag : 'div',
27936                     cls : 'column col-md-12',
27937                     html : this.fieldLabel
27938                 },
27939                 {
27940                     tag : 'div',
27941                     cls : 'column col-md-12',
27942                     cn : managerWidget
27943                 }
27944             ];
27945
27946             if(this.labelAlign == 'left'){
27947                 content = [
27948                     {
27949                         tag : 'div',
27950                         cls : 'column',
27951                         html : this.fieldLabel
27952                     },
27953                     {
27954                         tag : 'div',
27955                         cls : 'column',
27956                         cn : managerWidget
27957                     }
27958                 ];
27959                 
27960                 if(this.labelWidth > 12){
27961                     content[0].style = "width: " + this.labelWidth + 'px';
27962                 }
27963
27964                 if(this.labelWidth < 13 && this.labelmd == 0){
27965                     this.labelmd = this.labelWidth;
27966                 }
27967
27968                 if(this.labellg > 0){
27969                     content[0].cls += ' col-lg-' + this.labellg;
27970                     content[1].cls += ' col-lg-' + (12 - this.labellg);
27971                 }
27972
27973                 if(this.labelmd > 0){
27974                     content[0].cls += ' col-md-' + this.labelmd;
27975                     content[1].cls += ' col-md-' + (12 - this.labelmd);
27976                 }
27977
27978                 if(this.labelsm > 0){
27979                     content[0].cls += ' col-sm-' + this.labelsm;
27980                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
27981                 }
27982
27983                 if(this.labelxs > 0){
27984                     content[0].cls += ' col-xs-' + this.labelxs;
27985                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
27986                 }
27987                 
27988             }
27989         }
27990         
27991         var cfg = {
27992             tag : 'div',
27993             cls : 'row clearfix',
27994             cn : content
27995         };
27996         
27997         return cfg;
27998         
27999     },
28000     
28001     initEvents : function()
28002     {
28003         this.managerEl = this.el.select('.roo-document-manager', true).first();
28004         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28005         
28006         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28007         this.selectorEl.hide();
28008         
28009         if(this.multiple){
28010             this.selectorEl.attr('multiple', 'multiple');
28011         }
28012         
28013         this.selectorEl.on('change', this.onFileSelected, this);
28014         
28015         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28016         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28017         
28018         this.uploader.on('click', this.onUploaderClick, this);
28019         
28020         this.renderProgressDialog();
28021         
28022         var _this = this;
28023         
28024         window.addEventListener("resize", function() { _this.refresh(); } );
28025         
28026         this.fireEvent('initial', this);
28027     },
28028     
28029     renderProgressDialog : function()
28030     {
28031         var _this = this;
28032         
28033         this.progressDialog = new Roo.bootstrap.Modal({
28034             cls : 'roo-document-manager-progress-dialog',
28035             allow_close : false,
28036             title : '',
28037             buttons : [
28038                 {
28039                     name  :'cancel',
28040                     weight : 'danger',
28041                     html : 'Cancel'
28042                 }
28043             ], 
28044             listeners : { 
28045                 btnclick : function() {
28046                     _this.uploadCancel();
28047                     this.hide();
28048                 }
28049             }
28050         });
28051          
28052         this.progressDialog.render(Roo.get(document.body));
28053          
28054         this.progress = new Roo.bootstrap.Progress({
28055             cls : 'roo-document-manager-progress',
28056             active : true,
28057             striped : true
28058         });
28059         
28060         this.progress.render(this.progressDialog.getChildContainer());
28061         
28062         this.progressBar = new Roo.bootstrap.ProgressBar({
28063             cls : 'roo-document-manager-progress-bar',
28064             aria_valuenow : 0,
28065             aria_valuemin : 0,
28066             aria_valuemax : 12,
28067             panel : 'success'
28068         });
28069         
28070         this.progressBar.render(this.progress.getChildContainer());
28071     },
28072     
28073     onUploaderClick : function(e)
28074     {
28075         e.preventDefault();
28076      
28077         if(this.fireEvent('beforeselectfile', this) != false){
28078             this.selectorEl.dom.click();
28079         }
28080         
28081     },
28082     
28083     onFileSelected : function(e)
28084     {
28085         e.preventDefault();
28086         
28087         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28088             return;
28089         }
28090         
28091         Roo.each(this.selectorEl.dom.files, function(file){
28092             if(this.fireEvent('inspect', this, file) != false){
28093                 this.files.push(file);
28094             }
28095         }, this);
28096         
28097         this.queue();
28098         
28099     },
28100     
28101     queue : function()
28102     {
28103         this.selectorEl.dom.value = '';
28104         
28105         if(!this.files.length){
28106             return;
28107         }
28108         
28109         if(this.boxes > 0 && this.files.length > this.boxes){
28110             this.files = this.files.slice(0, this.boxes);
28111         }
28112         
28113         this.uploader.show();
28114         
28115         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28116             this.uploader.hide();
28117         }
28118         
28119         var _this = this;
28120         
28121         var files = [];
28122         
28123         var docs = [];
28124         
28125         Roo.each(this.files, function(file){
28126             
28127             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28128                 var f = this.renderPreview(file);
28129                 files.push(f);
28130                 return;
28131             }
28132             
28133             if(file.type.indexOf('image') != -1){
28134                 this.delegates.push(
28135                     (function(){
28136                         _this.process(file);
28137                     }).createDelegate(this)
28138                 );
28139         
28140                 return;
28141             }
28142             
28143             docs.push(
28144                 (function(){
28145                     _this.process(file);
28146                 }).createDelegate(this)
28147             );
28148             
28149         }, this);
28150         
28151         this.files = files;
28152         
28153         this.delegates = this.delegates.concat(docs);
28154         
28155         if(!this.delegates.length){
28156             this.refresh();
28157             return;
28158         }
28159         
28160         this.progressBar.aria_valuemax = this.delegates.length;
28161         
28162         this.arrange();
28163         
28164         return;
28165     },
28166     
28167     arrange : function()
28168     {
28169         if(!this.delegates.length){
28170             this.progressDialog.hide();
28171             this.refresh();
28172             return;
28173         }
28174         
28175         var delegate = this.delegates.shift();
28176         
28177         this.progressDialog.show();
28178         
28179         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28180         
28181         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28182         
28183         delegate();
28184     },
28185     
28186     refresh : function()
28187     {
28188         this.uploader.show();
28189         
28190         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28191             this.uploader.hide();
28192         }
28193         
28194         Roo.isTouch ? this.closable(false) : this.closable(true);
28195         
28196         this.fireEvent('refresh', this);
28197     },
28198     
28199     onRemove : function(e, el, o)
28200     {
28201         e.preventDefault();
28202         
28203         this.fireEvent('remove', this, o);
28204         
28205     },
28206     
28207     remove : function(o)
28208     {
28209         var files = [];
28210         
28211         Roo.each(this.files, function(file){
28212             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28213                 files.push(file);
28214                 return;
28215             }
28216
28217             o.target.remove();
28218
28219         }, this);
28220         
28221         this.files = files;
28222         
28223         this.refresh();
28224     },
28225     
28226     clear : function()
28227     {
28228         Roo.each(this.files, function(file){
28229             if(!file.target){
28230                 return;
28231             }
28232             
28233             file.target.remove();
28234
28235         }, this);
28236         
28237         this.files = [];
28238         
28239         this.refresh();
28240     },
28241     
28242     onClick : function(e, el, o)
28243     {
28244         e.preventDefault();
28245         
28246         this.fireEvent('click', this, o);
28247         
28248     },
28249     
28250     closable : function(closable)
28251     {
28252         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28253             
28254             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28255             
28256             if(closable){
28257                 el.show();
28258                 return;
28259             }
28260             
28261             el.hide();
28262             
28263         }, this);
28264     },
28265     
28266     xhrOnLoad : function(xhr)
28267     {
28268         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28269             el.remove();
28270         }, this);
28271         
28272         if (xhr.readyState !== 4) {
28273             this.arrange();
28274             this.fireEvent('exception', this, xhr);
28275             return;
28276         }
28277
28278         var response = Roo.decode(xhr.responseText);
28279         
28280         if(!response.success){
28281             this.arrange();
28282             this.fireEvent('exception', this, xhr);
28283             return;
28284         }
28285         
28286         var file = this.renderPreview(response.data);
28287         
28288         this.files.push(file);
28289         
28290         this.arrange();
28291         
28292         this.fireEvent('afterupload', this, xhr);
28293         
28294     },
28295     
28296     xhrOnError : function(xhr)
28297     {
28298         Roo.log('xhr on error');
28299         
28300         var response = Roo.decode(xhr.responseText);
28301           
28302         Roo.log(response);
28303         
28304         this.arrange();
28305     },
28306     
28307     process : function(file)
28308     {
28309         if(this.fireEvent('process', this, file) !== false){
28310             if(this.editable && file.type.indexOf('image') != -1){
28311                 this.fireEvent('edit', this, file);
28312                 return;
28313             }
28314
28315             this.uploadStart(file, false);
28316
28317             return;
28318         }
28319         
28320     },
28321     
28322     uploadStart : function(file, crop)
28323     {
28324         this.xhr = new XMLHttpRequest();
28325         
28326         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28327             this.arrange();
28328             return;
28329         }
28330         
28331         file.xhr = this.xhr;
28332             
28333         this.managerEl.createChild({
28334             tag : 'div',
28335             cls : 'roo-document-manager-loading',
28336             cn : [
28337                 {
28338                     tag : 'div',
28339                     tooltip : file.name,
28340                     cls : 'roo-document-manager-thumb',
28341                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28342                 }
28343             ]
28344
28345         });
28346
28347         this.xhr.open(this.method, this.url, true);
28348         
28349         var headers = {
28350             "Accept": "application/json",
28351             "Cache-Control": "no-cache",
28352             "X-Requested-With": "XMLHttpRequest"
28353         };
28354         
28355         for (var headerName in headers) {
28356             var headerValue = headers[headerName];
28357             if (headerValue) {
28358                 this.xhr.setRequestHeader(headerName, headerValue);
28359             }
28360         }
28361         
28362         var _this = this;
28363         
28364         this.xhr.onload = function()
28365         {
28366             _this.xhrOnLoad(_this.xhr);
28367         }
28368         
28369         this.xhr.onerror = function()
28370         {
28371             _this.xhrOnError(_this.xhr);
28372         }
28373         
28374         var formData = new FormData();
28375
28376         formData.append('returnHTML', 'NO');
28377         
28378         if(crop){
28379             formData.append('crop', crop);
28380         }
28381         
28382         formData.append(this.paramName, file, file.name);
28383         
28384         var options = {
28385             file : file, 
28386             manually : false
28387         };
28388         
28389         if(this.fireEvent('prepare', this, formData, options) != false){
28390             
28391             if(options.manually){
28392                 return;
28393             }
28394             
28395             this.xhr.send(formData);
28396             return;
28397         };
28398         
28399         this.uploadCancel();
28400     },
28401     
28402     uploadCancel : function()
28403     {
28404         if (this.xhr) {
28405             this.xhr.abort();
28406         }
28407         
28408         this.delegates = [];
28409         
28410         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28411             el.remove();
28412         }, this);
28413         
28414         this.arrange();
28415     },
28416     
28417     renderPreview : function(file)
28418     {
28419         if(typeof(file.target) != 'undefined' && file.target){
28420             return file;
28421         }
28422         
28423         var previewEl = this.managerEl.createChild({
28424             tag : 'div',
28425             cls : 'roo-document-manager-preview',
28426             cn : [
28427                 {
28428                     tag : 'div',
28429                     tooltip : file[this.toolTipName],
28430                     cls : 'roo-document-manager-thumb',
28431                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28432                 },
28433                 {
28434                     tag : 'button',
28435                     cls : 'close',
28436                     html : '<i class="fa fa-times-circle"></i>'
28437                 }
28438             ]
28439         });
28440
28441         var close = previewEl.select('button.close', true).first();
28442
28443         close.on('click', this.onRemove, this, file);
28444
28445         file.target = previewEl;
28446
28447         var image = previewEl.select('img', true).first();
28448         
28449         var _this = this;
28450         
28451         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28452         
28453         image.on('click', this.onClick, this, file);
28454         
28455         return file;
28456         
28457     },
28458     
28459     onPreviewLoad : function(file, image)
28460     {
28461         if(typeof(file.target) == 'undefined' || !file.target){
28462             return;
28463         }
28464         
28465         var width = image.dom.naturalWidth || image.dom.width;
28466         var height = image.dom.naturalHeight || image.dom.height;
28467         
28468         if(width > height){
28469             file.target.addClass('wide');
28470             return;
28471         }
28472         
28473         file.target.addClass('tall');
28474         return;
28475         
28476     },
28477     
28478     uploadFromSource : function(file, crop)
28479     {
28480         this.xhr = new XMLHttpRequest();
28481         
28482         this.managerEl.createChild({
28483             tag : 'div',
28484             cls : 'roo-document-manager-loading',
28485             cn : [
28486                 {
28487                     tag : 'div',
28488                     tooltip : file.name,
28489                     cls : 'roo-document-manager-thumb',
28490                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28491                 }
28492             ]
28493
28494         });
28495
28496         this.xhr.open(this.method, this.url, true);
28497         
28498         var headers = {
28499             "Accept": "application/json",
28500             "Cache-Control": "no-cache",
28501             "X-Requested-With": "XMLHttpRequest"
28502         };
28503         
28504         for (var headerName in headers) {
28505             var headerValue = headers[headerName];
28506             if (headerValue) {
28507                 this.xhr.setRequestHeader(headerName, headerValue);
28508             }
28509         }
28510         
28511         var _this = this;
28512         
28513         this.xhr.onload = function()
28514         {
28515             _this.xhrOnLoad(_this.xhr);
28516         }
28517         
28518         this.xhr.onerror = function()
28519         {
28520             _this.xhrOnError(_this.xhr);
28521         }
28522         
28523         var formData = new FormData();
28524
28525         formData.append('returnHTML', 'NO');
28526         
28527         formData.append('crop', crop);
28528         
28529         if(typeof(file.filename) != 'undefined'){
28530             formData.append('filename', file.filename);
28531         }
28532         
28533         if(typeof(file.mimetype) != 'undefined'){
28534             formData.append('mimetype', file.mimetype);
28535         }
28536         
28537         Roo.log(formData);
28538         
28539         if(this.fireEvent('prepare', this, formData) != false){
28540             this.xhr.send(formData);
28541         };
28542     }
28543 });
28544
28545 /*
28546 * Licence: LGPL
28547 */
28548
28549 /**
28550  * @class Roo.bootstrap.DocumentViewer
28551  * @extends Roo.bootstrap.Component
28552  * Bootstrap DocumentViewer class
28553  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28554  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28555  * 
28556  * @constructor
28557  * Create a new DocumentViewer
28558  * @param {Object} config The config object
28559  */
28560
28561 Roo.bootstrap.DocumentViewer = function(config){
28562     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28563     
28564     this.addEvents({
28565         /**
28566          * @event initial
28567          * Fire after initEvent
28568          * @param {Roo.bootstrap.DocumentViewer} this
28569          */
28570         "initial" : true,
28571         /**
28572          * @event click
28573          * Fire after click
28574          * @param {Roo.bootstrap.DocumentViewer} this
28575          */
28576         "click" : true,
28577         /**
28578          * @event download
28579          * Fire after download button
28580          * @param {Roo.bootstrap.DocumentViewer} this
28581          */
28582         "download" : true,
28583         /**
28584          * @event trash
28585          * Fire after trash button
28586          * @param {Roo.bootstrap.DocumentViewer} this
28587          */
28588         "trash" : true
28589         
28590     });
28591 };
28592
28593 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28594     
28595     showDownload : true,
28596     
28597     showTrash : true,
28598     
28599     getAutoCreate : function()
28600     {
28601         var cfg = {
28602             tag : 'div',
28603             cls : 'roo-document-viewer',
28604             cn : [
28605                 {
28606                     tag : 'div',
28607                     cls : 'roo-document-viewer-body',
28608                     cn : [
28609                         {
28610                             tag : 'div',
28611                             cls : 'roo-document-viewer-thumb',
28612                             cn : [
28613                                 {
28614                                     tag : 'img',
28615                                     cls : 'roo-document-viewer-image'
28616                                 }
28617                             ]
28618                         }
28619                     ]
28620                 },
28621                 {
28622                     tag : 'div',
28623                     cls : 'roo-document-viewer-footer',
28624                     cn : {
28625                         tag : 'div',
28626                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28627                         cn : [
28628                             {
28629                                 tag : 'div',
28630                                 cls : 'btn-group roo-document-viewer-download',
28631                                 cn : [
28632                                     {
28633                                         tag : 'button',
28634                                         cls : 'btn btn-default',
28635                                         html : '<i class="fa fa-download"></i>'
28636                                     }
28637                                 ]
28638                             },
28639                             {
28640                                 tag : 'div',
28641                                 cls : 'btn-group roo-document-viewer-trash',
28642                                 cn : [
28643                                     {
28644                                         tag : 'button',
28645                                         cls : 'btn btn-default',
28646                                         html : '<i class="fa fa-trash"></i>'
28647                                     }
28648                                 ]
28649                             }
28650                         ]
28651                     }
28652                 }
28653             ]
28654         };
28655         
28656         return cfg;
28657     },
28658     
28659     initEvents : function()
28660     {
28661         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28662         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28663         
28664         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28665         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28666         
28667         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28668         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28669         
28670         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28671         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28672         
28673         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28674         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28675         
28676         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28677         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28678         
28679         this.bodyEl.on('click', this.onClick, this);
28680         this.downloadBtn.on('click', this.onDownload, this);
28681         this.trashBtn.on('click', this.onTrash, this);
28682         
28683         this.downloadBtn.hide();
28684         this.trashBtn.hide();
28685         
28686         if(this.showDownload){
28687             this.downloadBtn.show();
28688         }
28689         
28690         if(this.showTrash){
28691             this.trashBtn.show();
28692         }
28693         
28694         if(!this.showDownload && !this.showTrash) {
28695             this.footerEl.hide();
28696         }
28697         
28698     },
28699     
28700     initial : function()
28701     {
28702         this.fireEvent('initial', this);
28703         
28704     },
28705     
28706     onClick : function(e)
28707     {
28708         e.preventDefault();
28709         
28710         this.fireEvent('click', this);
28711     },
28712     
28713     onDownload : function(e)
28714     {
28715         e.preventDefault();
28716         
28717         this.fireEvent('download', this);
28718     },
28719     
28720     onTrash : function(e)
28721     {
28722         e.preventDefault();
28723         
28724         this.fireEvent('trash', this);
28725     }
28726     
28727 });
28728 /*
28729  * - LGPL
28730  *
28731  * nav progress bar
28732  * 
28733  */
28734
28735 /**
28736  * @class Roo.bootstrap.NavProgressBar
28737  * @extends Roo.bootstrap.Component
28738  * Bootstrap NavProgressBar class
28739  * 
28740  * @constructor
28741  * Create a new nav progress bar
28742  * @param {Object} config The config object
28743  */
28744
28745 Roo.bootstrap.NavProgressBar = function(config){
28746     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28747
28748     this.bullets = this.bullets || [];
28749    
28750 //    Roo.bootstrap.NavProgressBar.register(this);
28751      this.addEvents({
28752         /**
28753              * @event changed
28754              * Fires when the active item changes
28755              * @param {Roo.bootstrap.NavProgressBar} this
28756              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28757              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28758          */
28759         'changed': true
28760      });
28761     
28762 };
28763
28764 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28765     
28766     bullets : [],
28767     barItems : [],
28768     
28769     getAutoCreate : function()
28770     {
28771         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28772         
28773         cfg = {
28774             tag : 'div',
28775             cls : 'roo-navigation-bar-group',
28776             cn : [
28777                 {
28778                     tag : 'div',
28779                     cls : 'roo-navigation-top-bar'
28780                 },
28781                 {
28782                     tag : 'div',
28783                     cls : 'roo-navigation-bullets-bar',
28784                     cn : [
28785                         {
28786                             tag : 'ul',
28787                             cls : 'roo-navigation-bar'
28788                         }
28789                     ]
28790                 },
28791                 
28792                 {
28793                     tag : 'div',
28794                     cls : 'roo-navigation-bottom-bar'
28795                 }
28796             ]
28797             
28798         };
28799         
28800         return cfg;
28801         
28802     },
28803     
28804     initEvents: function() 
28805     {
28806         
28807     },
28808     
28809     onRender : function(ct, position) 
28810     {
28811         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28812         
28813         if(this.bullets.length){
28814             Roo.each(this.bullets, function(b){
28815                this.addItem(b);
28816             }, this);
28817         }
28818         
28819         this.format();
28820         
28821     },
28822     
28823     addItem : function(cfg)
28824     {
28825         var item = new Roo.bootstrap.NavProgressItem(cfg);
28826         
28827         item.parentId = this.id;
28828         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28829         
28830         if(cfg.html){
28831             var top = new Roo.bootstrap.Element({
28832                 tag : 'div',
28833                 cls : 'roo-navigation-bar-text'
28834             });
28835             
28836             var bottom = new Roo.bootstrap.Element({
28837                 tag : 'div',
28838                 cls : 'roo-navigation-bar-text'
28839             });
28840             
28841             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28842             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28843             
28844             var topText = new Roo.bootstrap.Element({
28845                 tag : 'span',
28846                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28847             });
28848             
28849             var bottomText = new Roo.bootstrap.Element({
28850                 tag : 'span',
28851                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28852             });
28853             
28854             topText.onRender(top.el, null);
28855             bottomText.onRender(bottom.el, null);
28856             
28857             item.topEl = top;
28858             item.bottomEl = bottom;
28859         }
28860         
28861         this.barItems.push(item);
28862         
28863         return item;
28864     },
28865     
28866     getActive : function()
28867     {
28868         var active = false;
28869         
28870         Roo.each(this.barItems, function(v){
28871             
28872             if (!v.isActive()) {
28873                 return;
28874             }
28875             
28876             active = v;
28877             return false;
28878             
28879         });
28880         
28881         return active;
28882     },
28883     
28884     setActiveItem : function(item)
28885     {
28886         var prev = false;
28887         
28888         Roo.each(this.barItems, function(v){
28889             if (v.rid == item.rid) {
28890                 return ;
28891             }
28892             
28893             if (v.isActive()) {
28894                 v.setActive(false);
28895                 prev = v;
28896             }
28897         });
28898
28899         item.setActive(true);
28900         
28901         this.fireEvent('changed', this, item, prev);
28902     },
28903     
28904     getBarItem: function(rid)
28905     {
28906         var ret = false;
28907         
28908         Roo.each(this.barItems, function(e) {
28909             if (e.rid != rid) {
28910                 return;
28911             }
28912             
28913             ret =  e;
28914             return false;
28915         });
28916         
28917         return ret;
28918     },
28919     
28920     indexOfItem : function(item)
28921     {
28922         var index = false;
28923         
28924         Roo.each(this.barItems, function(v, i){
28925             
28926             if (v.rid != item.rid) {
28927                 return;
28928             }
28929             
28930             index = i;
28931             return false
28932         });
28933         
28934         return index;
28935     },
28936     
28937     setActiveNext : function()
28938     {
28939         var i = this.indexOfItem(this.getActive());
28940         
28941         if (i > this.barItems.length) {
28942             return;
28943         }
28944         
28945         this.setActiveItem(this.barItems[i+1]);
28946     },
28947     
28948     setActivePrev : function()
28949     {
28950         var i = this.indexOfItem(this.getActive());
28951         
28952         if (i  < 1) {
28953             return;
28954         }
28955         
28956         this.setActiveItem(this.barItems[i-1]);
28957     },
28958     
28959     format : function()
28960     {
28961         if(!this.barItems.length){
28962             return;
28963         }
28964      
28965         var width = 100 / this.barItems.length;
28966         
28967         Roo.each(this.barItems, function(i){
28968             i.el.setStyle('width', width + '%');
28969             i.topEl.el.setStyle('width', width + '%');
28970             i.bottomEl.el.setStyle('width', width + '%');
28971         }, this);
28972         
28973     }
28974     
28975 });
28976 /*
28977  * - LGPL
28978  *
28979  * Nav Progress Item
28980  * 
28981  */
28982
28983 /**
28984  * @class Roo.bootstrap.NavProgressItem
28985  * @extends Roo.bootstrap.Component
28986  * Bootstrap NavProgressItem class
28987  * @cfg {String} rid the reference id
28988  * @cfg {Boolean} active (true|false) Is item active default false
28989  * @cfg {Boolean} disabled (true|false) Is item active default false
28990  * @cfg {String} html
28991  * @cfg {String} position (top|bottom) text position default bottom
28992  * @cfg {String} icon show icon instead of number
28993  * 
28994  * @constructor
28995  * Create a new NavProgressItem
28996  * @param {Object} config The config object
28997  */
28998 Roo.bootstrap.NavProgressItem = function(config){
28999     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29000     this.addEvents({
29001         // raw events
29002         /**
29003          * @event click
29004          * The raw click event for the entire grid.
29005          * @param {Roo.bootstrap.NavProgressItem} this
29006          * @param {Roo.EventObject} e
29007          */
29008         "click" : true
29009     });
29010    
29011 };
29012
29013 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29014     
29015     rid : '',
29016     active : false,
29017     disabled : false,
29018     html : '',
29019     position : 'bottom',
29020     icon : false,
29021     
29022     getAutoCreate : function()
29023     {
29024         var iconCls = 'roo-navigation-bar-item-icon';
29025         
29026         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29027         
29028         var cfg = {
29029             tag: 'li',
29030             cls: 'roo-navigation-bar-item',
29031             cn : [
29032                 {
29033                     tag : 'i',
29034                     cls : iconCls
29035                 }
29036             ]
29037         };
29038         
29039         if(this.active){
29040             cfg.cls += ' active';
29041         }
29042         if(this.disabled){
29043             cfg.cls += ' disabled';
29044         }
29045         
29046         return cfg;
29047     },
29048     
29049     disable : function()
29050     {
29051         this.setDisabled(true);
29052     },
29053     
29054     enable : function()
29055     {
29056         this.setDisabled(false);
29057     },
29058     
29059     initEvents: function() 
29060     {
29061         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29062         
29063         this.iconEl.on('click', this.onClick, this);
29064     },
29065     
29066     onClick : function(e)
29067     {
29068         e.preventDefault();
29069         
29070         if(this.disabled){
29071             return;
29072         }
29073         
29074         if(this.fireEvent('click', this, e) === false){
29075             return;
29076         };
29077         
29078         this.parent().setActiveItem(this);
29079     },
29080     
29081     isActive: function () 
29082     {
29083         return this.active;
29084     },
29085     
29086     setActive : function(state)
29087     {
29088         if(this.active == state){
29089             return;
29090         }
29091         
29092         this.active = state;
29093         
29094         if (state) {
29095             this.el.addClass('active');
29096             return;
29097         }
29098         
29099         this.el.removeClass('active');
29100         
29101         return;
29102     },
29103     
29104     setDisabled : function(state)
29105     {
29106         if(this.disabled == state){
29107             return;
29108         }
29109         
29110         this.disabled = state;
29111         
29112         if (state) {
29113             this.el.addClass('disabled');
29114             return;
29115         }
29116         
29117         this.el.removeClass('disabled');
29118     },
29119     
29120     tooltipEl : function()
29121     {
29122         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29123     }
29124 });
29125  
29126
29127  /*
29128  * - LGPL
29129  *
29130  * FieldLabel
29131  * 
29132  */
29133
29134 /**
29135  * @class Roo.bootstrap.FieldLabel
29136  * @extends Roo.bootstrap.Component
29137  * Bootstrap FieldLabel class
29138  * @cfg {String} html contents of the element
29139  * @cfg {String} tag tag of the element default label
29140  * @cfg {String} cls class of the element
29141  * @cfg {String} target label target 
29142  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29143  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29144  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29145  * @cfg {String} iconTooltip default "This field is required"
29146  * 
29147  * @constructor
29148  * Create a new FieldLabel
29149  * @param {Object} config The config object
29150  */
29151
29152 Roo.bootstrap.FieldLabel = function(config){
29153     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29154     
29155     this.addEvents({
29156             /**
29157              * @event invalid
29158              * Fires after the field has been marked as invalid.
29159              * @param {Roo.form.FieldLabel} this
29160              * @param {String} msg The validation message
29161              */
29162             invalid : true,
29163             /**
29164              * @event valid
29165              * Fires after the field has been validated with no errors.
29166              * @param {Roo.form.FieldLabel} this
29167              */
29168             valid : true
29169         });
29170 };
29171
29172 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29173     
29174     tag: 'label',
29175     cls: '',
29176     html: '',
29177     target: '',
29178     allowBlank : true,
29179     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29180     validClass : 'text-success fa fa-lg fa-check',
29181     iconTooltip : 'This field is required',
29182     
29183     getAutoCreate : function(){
29184         
29185         var cfg = {
29186             tag : this.tag,
29187             cls : 'roo-bootstrap-field-label ' + this.cls,
29188             for : this.target,
29189             cn : [
29190                 {
29191                     tag : 'i',
29192                     cls : '',
29193                     tooltip : this.iconTooltip
29194                 },
29195                 {
29196                     tag : 'span',
29197                     html : this.html
29198                 }
29199             ] 
29200         };
29201         
29202         return cfg;
29203     },
29204     
29205     initEvents: function() 
29206     {
29207         Roo.bootstrap.Element.superclass.initEvents.call(this);
29208         
29209         this.iconEl = this.el.select('i', true).first();
29210         
29211         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29212         
29213         Roo.bootstrap.FieldLabel.register(this);
29214     },
29215     
29216     /**
29217      * Mark this field as valid
29218      */
29219     markValid : function()
29220     {
29221         this.iconEl.show();
29222         
29223         this.iconEl.removeClass(this.invalidClass);
29224         
29225         this.iconEl.addClass(this.validClass);
29226         
29227         this.fireEvent('valid', this);
29228     },
29229     
29230     /**
29231      * Mark this field as invalid
29232      * @param {String} msg The validation message
29233      */
29234     markInvalid : function(msg)
29235     {
29236         this.iconEl.show();
29237         
29238         this.iconEl.removeClass(this.validClass);
29239         
29240         this.iconEl.addClass(this.invalidClass);
29241         
29242         this.fireEvent('invalid', this, msg);
29243     }
29244     
29245    
29246 });
29247
29248 Roo.apply(Roo.bootstrap.FieldLabel, {
29249     
29250     groups: {},
29251     
29252      /**
29253     * register a FieldLabel Group
29254     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29255     */
29256     register : function(label)
29257     {
29258         if(this.groups.hasOwnProperty(label.target)){
29259             return;
29260         }
29261      
29262         this.groups[label.target] = label;
29263         
29264     },
29265     /**
29266     * fetch a FieldLabel Group based on the target
29267     * @param {string} target
29268     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29269     */
29270     get: function(target) {
29271         if (typeof(this.groups[target]) == 'undefined') {
29272             return false;
29273         }
29274         
29275         return this.groups[target] ;
29276     }
29277 });
29278
29279  
29280
29281  /*
29282  * - LGPL
29283  *
29284  * page DateSplitField.
29285  * 
29286  */
29287
29288
29289 /**
29290  * @class Roo.bootstrap.DateSplitField
29291  * @extends Roo.bootstrap.Component
29292  * Bootstrap DateSplitField class
29293  * @cfg {string} fieldLabel - the label associated
29294  * @cfg {Number} labelWidth set the width of label (0-12)
29295  * @cfg {String} labelAlign (top|left)
29296  * @cfg {Boolean} dayAllowBlank (true|false) default false
29297  * @cfg {Boolean} monthAllowBlank (true|false) default false
29298  * @cfg {Boolean} yearAllowBlank (true|false) default false
29299  * @cfg {string} dayPlaceholder 
29300  * @cfg {string} monthPlaceholder
29301  * @cfg {string} yearPlaceholder
29302  * @cfg {string} dayFormat default 'd'
29303  * @cfg {string} monthFormat default 'm'
29304  * @cfg {string} yearFormat default 'Y'
29305  * @cfg {Number} labellg set the width of label (1-12)
29306  * @cfg {Number} labelmd set the width of label (1-12)
29307  * @cfg {Number} labelsm set the width of label (1-12)
29308  * @cfg {Number} labelxs set the width of label (1-12)
29309
29310  *     
29311  * @constructor
29312  * Create a new DateSplitField
29313  * @param {Object} config The config object
29314  */
29315
29316 Roo.bootstrap.DateSplitField = function(config){
29317     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29318     
29319     this.addEvents({
29320         // raw events
29321          /**
29322          * @event years
29323          * getting the data of years
29324          * @param {Roo.bootstrap.DateSplitField} this
29325          * @param {Object} years
29326          */
29327         "years" : true,
29328         /**
29329          * @event days
29330          * getting the data of days
29331          * @param {Roo.bootstrap.DateSplitField} this
29332          * @param {Object} days
29333          */
29334         "days" : true,
29335         /**
29336          * @event invalid
29337          * Fires after the field has been marked as invalid.
29338          * @param {Roo.form.Field} this
29339          * @param {String} msg The validation message
29340          */
29341         invalid : true,
29342        /**
29343          * @event valid
29344          * Fires after the field has been validated with no errors.
29345          * @param {Roo.form.Field} this
29346          */
29347         valid : true
29348     });
29349 };
29350
29351 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29352     
29353     fieldLabel : '',
29354     labelAlign : 'top',
29355     labelWidth : 3,
29356     dayAllowBlank : false,
29357     monthAllowBlank : false,
29358     yearAllowBlank : false,
29359     dayPlaceholder : '',
29360     monthPlaceholder : '',
29361     yearPlaceholder : '',
29362     dayFormat : 'd',
29363     monthFormat : 'm',
29364     yearFormat : 'Y',
29365     isFormField : true,
29366     labellg : 0,
29367     labelmd : 0,
29368     labelsm : 0,
29369     labelxs : 0,
29370     
29371     getAutoCreate : function()
29372     {
29373         var cfg = {
29374             tag : 'div',
29375             cls : 'row roo-date-split-field-group',
29376             cn : [
29377                 {
29378                     tag : 'input',
29379                     type : 'hidden',
29380                     cls : 'form-hidden-field roo-date-split-field-group-value',
29381                     name : this.name
29382                 }
29383             ]
29384         };
29385         
29386         var labelCls = 'col-md-12';
29387         var contentCls = 'col-md-4';
29388         
29389         if(this.fieldLabel){
29390             
29391             var label = {
29392                 tag : 'div',
29393                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29394                 cn : [
29395                     {
29396                         tag : 'label',
29397                         html : this.fieldLabel
29398                     }
29399                 ]
29400             };
29401             
29402             if(this.labelAlign == 'left'){
29403             
29404                 if(this.labelWidth > 12){
29405                     label.style = "width: " + this.labelWidth + 'px';
29406                 }
29407
29408                 if(this.labelWidth < 13 && this.labelmd == 0){
29409                     this.labelmd = this.labelWidth;
29410                 }
29411
29412                 if(this.labellg > 0){
29413                     labelCls = ' col-lg-' + this.labellg;
29414                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29415                 }
29416
29417                 if(this.labelmd > 0){
29418                     labelCls = ' col-md-' + this.labelmd;
29419                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29420                 }
29421
29422                 if(this.labelsm > 0){
29423                     labelCls = ' col-sm-' + this.labelsm;
29424                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29425                 }
29426
29427                 if(this.labelxs > 0){
29428                     labelCls = ' col-xs-' + this.labelxs;
29429                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29430                 }
29431             }
29432             
29433             label.cls += ' ' + labelCls;
29434             
29435             cfg.cn.push(label);
29436         }
29437         
29438         Roo.each(['day', 'month', 'year'], function(t){
29439             cfg.cn.push({
29440                 tag : 'div',
29441                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29442             });
29443         }, this);
29444         
29445         return cfg;
29446     },
29447     
29448     inputEl: function ()
29449     {
29450         return this.el.select('.roo-date-split-field-group-value', true).first();
29451     },
29452     
29453     onRender : function(ct, position) 
29454     {
29455         var _this = this;
29456         
29457         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29458         
29459         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29460         
29461         this.dayField = new Roo.bootstrap.ComboBox({
29462             allowBlank : this.dayAllowBlank,
29463             alwaysQuery : true,
29464             displayField : 'value',
29465             editable : false,
29466             fieldLabel : '',
29467             forceSelection : true,
29468             mode : 'local',
29469             placeholder : this.dayPlaceholder,
29470             selectOnFocus : true,
29471             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29472             triggerAction : 'all',
29473             typeAhead : true,
29474             valueField : 'value',
29475             store : new Roo.data.SimpleStore({
29476                 data : (function() {    
29477                     var days = [];
29478                     _this.fireEvent('days', _this, days);
29479                     return days;
29480                 })(),
29481                 fields : [ 'value' ]
29482             }),
29483             listeners : {
29484                 select : function (_self, record, index)
29485                 {
29486                     _this.setValue(_this.getValue());
29487                 }
29488             }
29489         });
29490
29491         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29492         
29493         this.monthField = new Roo.bootstrap.MonthField({
29494             after : '<i class=\"fa fa-calendar\"></i>',
29495             allowBlank : this.monthAllowBlank,
29496             placeholder : this.monthPlaceholder,
29497             readOnly : true,
29498             listeners : {
29499                 render : function (_self)
29500                 {
29501                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29502                         e.preventDefault();
29503                         _self.focus();
29504                     });
29505                 },
29506                 select : function (_self, oldvalue, newvalue)
29507                 {
29508                     _this.setValue(_this.getValue());
29509                 }
29510             }
29511         });
29512         
29513         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29514         
29515         this.yearField = new Roo.bootstrap.ComboBox({
29516             allowBlank : this.yearAllowBlank,
29517             alwaysQuery : true,
29518             displayField : 'value',
29519             editable : false,
29520             fieldLabel : '',
29521             forceSelection : true,
29522             mode : 'local',
29523             placeholder : this.yearPlaceholder,
29524             selectOnFocus : true,
29525             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29526             triggerAction : 'all',
29527             typeAhead : true,
29528             valueField : 'value',
29529             store : new Roo.data.SimpleStore({
29530                 data : (function() {
29531                     var years = [];
29532                     _this.fireEvent('years', _this, years);
29533                     return years;
29534                 })(),
29535                 fields : [ 'value' ]
29536             }),
29537             listeners : {
29538                 select : function (_self, record, index)
29539                 {
29540                     _this.setValue(_this.getValue());
29541                 }
29542             }
29543         });
29544
29545         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29546     },
29547     
29548     setValue : function(v, format)
29549     {
29550         this.inputEl.dom.value = v;
29551         
29552         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29553         
29554         var d = Date.parseDate(v, f);
29555         
29556         if(!d){
29557             this.validate();
29558             return;
29559         }
29560         
29561         this.setDay(d.format(this.dayFormat));
29562         this.setMonth(d.format(this.monthFormat));
29563         this.setYear(d.format(this.yearFormat));
29564         
29565         this.validate();
29566         
29567         return;
29568     },
29569     
29570     setDay : function(v)
29571     {
29572         this.dayField.setValue(v);
29573         this.inputEl.dom.value = this.getValue();
29574         this.validate();
29575         return;
29576     },
29577     
29578     setMonth : function(v)
29579     {
29580         this.monthField.setValue(v, true);
29581         this.inputEl.dom.value = this.getValue();
29582         this.validate();
29583         return;
29584     },
29585     
29586     setYear : function(v)
29587     {
29588         this.yearField.setValue(v);
29589         this.inputEl.dom.value = this.getValue();
29590         this.validate();
29591         return;
29592     },
29593     
29594     getDay : function()
29595     {
29596         return this.dayField.getValue();
29597     },
29598     
29599     getMonth : function()
29600     {
29601         return this.monthField.getValue();
29602     },
29603     
29604     getYear : function()
29605     {
29606         return this.yearField.getValue();
29607     },
29608     
29609     getValue : function()
29610     {
29611         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29612         
29613         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29614         
29615         return date;
29616     },
29617     
29618     reset : function()
29619     {
29620         this.setDay('');
29621         this.setMonth('');
29622         this.setYear('');
29623         this.inputEl.dom.value = '';
29624         this.validate();
29625         return;
29626     },
29627     
29628     validate : function()
29629     {
29630         var d = this.dayField.validate();
29631         var m = this.monthField.validate();
29632         var y = this.yearField.validate();
29633         
29634         var valid = true;
29635         
29636         if(
29637                 (!this.dayAllowBlank && !d) ||
29638                 (!this.monthAllowBlank && !m) ||
29639                 (!this.yearAllowBlank && !y)
29640         ){
29641             valid = false;
29642         }
29643         
29644         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29645             return valid;
29646         }
29647         
29648         if(valid){
29649             this.markValid();
29650             return valid;
29651         }
29652         
29653         this.markInvalid();
29654         
29655         return valid;
29656     },
29657     
29658     markValid : function()
29659     {
29660         
29661         var label = this.el.select('label', true).first();
29662         var icon = this.el.select('i.fa-star', true).first();
29663
29664         if(label && icon){
29665             icon.remove();
29666         }
29667         
29668         this.fireEvent('valid', this);
29669     },
29670     
29671      /**
29672      * Mark this field as invalid
29673      * @param {String} msg The validation message
29674      */
29675     markInvalid : function(msg)
29676     {
29677         
29678         var label = this.el.select('label', true).first();
29679         var icon = this.el.select('i.fa-star', true).first();
29680
29681         if(label && !icon){
29682             this.el.select('.roo-date-split-field-label', true).createChild({
29683                 tag : 'i',
29684                 cls : 'text-danger fa fa-lg fa-star',
29685                 tooltip : 'This field is required',
29686                 style : 'margin-right:5px;'
29687             }, label, true);
29688         }
29689         
29690         this.fireEvent('invalid', this, msg);
29691     },
29692     
29693     clearInvalid : function()
29694     {
29695         var label = this.el.select('label', true).first();
29696         var icon = this.el.select('i.fa-star', true).first();
29697
29698         if(label && icon){
29699             icon.remove();
29700         }
29701         
29702         this.fireEvent('valid', this);
29703     },
29704     
29705     getName: function()
29706     {
29707         return this.name;
29708     }
29709     
29710 });
29711
29712  /**
29713  *
29714  * This is based on 
29715  * http://masonry.desandro.com
29716  *
29717  * The idea is to render all the bricks based on vertical width...
29718  *
29719  * The original code extends 'outlayer' - we might need to use that....
29720  * 
29721  */
29722
29723
29724 /**
29725  * @class Roo.bootstrap.LayoutMasonry
29726  * @extends Roo.bootstrap.Component
29727  * Bootstrap Layout Masonry class
29728  * 
29729  * @constructor
29730  * Create a new Element
29731  * @param {Object} config The config object
29732  */
29733
29734 Roo.bootstrap.LayoutMasonry = function(config){
29735     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29736     
29737     this.bricks = [];
29738     
29739     this.addEvents({
29740         // raw events
29741         /**
29742          * @event layout
29743          * Fire after layout the items
29744          * @param {Roo.bootstrap.LayoutMasonry} this
29745          * @param {Roo.EventObject} e
29746          */
29747         "layout" : true
29748     });
29749     
29750 };
29751
29752 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29753     
29754     /**
29755      * @cfg {Boolean} isLayoutInstant = no animation?
29756      */   
29757     isLayoutInstant : false, // needed?
29758    
29759     /**
29760      * @cfg {Number} boxWidth  width of the columns
29761      */   
29762     boxWidth : 450,
29763     
29764       /**
29765      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29766      */   
29767     boxHeight : 0,
29768     
29769     /**
29770      * @cfg {Number} padWidth padding below box..
29771      */   
29772     padWidth : 10, 
29773     
29774     /**
29775      * @cfg {Number} gutter gutter width..
29776      */   
29777     gutter : 10,
29778     
29779      /**
29780      * @cfg {Number} maxCols maximum number of columns
29781      */   
29782     
29783     maxCols: 0,
29784     
29785     /**
29786      * @cfg {Boolean} isAutoInitial defalut true
29787      */   
29788     isAutoInitial : true, 
29789     
29790     containerWidth: 0,
29791     
29792     /**
29793      * @cfg {Boolean} isHorizontal defalut false
29794      */   
29795     isHorizontal : false, 
29796
29797     currentSize : null,
29798     
29799     tag: 'div',
29800     
29801     cls: '',
29802     
29803     bricks: null, //CompositeElement
29804     
29805     cols : 1,
29806     
29807     _isLayoutInited : false,
29808     
29809 //    isAlternative : false, // only use for vertical layout...
29810     
29811     /**
29812      * @cfg {Number} alternativePadWidth padding below box..
29813      */   
29814     alternativePadWidth : 50, 
29815     
29816     getAutoCreate : function(){
29817         
29818         var cfg = {
29819             tag: this.tag,
29820             cls: 'blog-masonary-wrapper ' + this.cls,
29821             cn : {
29822                 cls : 'mas-boxes masonary'
29823             }
29824         };
29825         
29826         return cfg;
29827     },
29828     
29829     getChildContainer: function( )
29830     {
29831         if (this.boxesEl) {
29832             return this.boxesEl;
29833         }
29834         
29835         this.boxesEl = this.el.select('.mas-boxes').first();
29836         
29837         return this.boxesEl;
29838     },
29839     
29840     
29841     initEvents : function()
29842     {
29843         var _this = this;
29844         
29845         if(this.isAutoInitial){
29846             Roo.log('hook children rendered');
29847             this.on('childrenrendered', function() {
29848                 Roo.log('children rendered');
29849                 _this.initial();
29850             } ,this);
29851         }
29852     },
29853     
29854     initial : function()
29855     {
29856         this.currentSize = this.el.getBox(true);
29857         
29858         Roo.EventManager.onWindowResize(this.resize, this); 
29859
29860         if(!this.isAutoInitial){
29861             this.layout();
29862             return;
29863         }
29864         
29865         this.layout();
29866         
29867         return;
29868         //this.layout.defer(500,this);
29869         
29870     },
29871     
29872     addItem : function(cfg)
29873     {
29874         Roo.log('adding item!!!!!!!!!!!!!!!!!!!!!!!!!!');
29875         var brick = Roo.factory(cfg);
29876         brick.render(this.getChildContainer());
29877     },
29878     
29879     resize : function()
29880     {
29881         var cs = this.el.getBox(true);
29882         
29883         if (
29884                 this.currentSize.width == cs.width && 
29885                 this.currentSize.x == cs.x && 
29886                 this.currentSize.height == cs.height && 
29887                 this.currentSize.y == cs.y 
29888         ) {
29889             Roo.log("no change in with or X or Y");
29890             return;
29891         }
29892         
29893         this.currentSize = cs;
29894         
29895         this.layout();
29896         
29897     },
29898     
29899     layout : function()
29900     {   
29901         this._resetLayout();
29902         
29903         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29904         
29905         this.layoutItems( isInstant );
29906       
29907         this._isLayoutInited = true;
29908         
29909         this.fireEvent('layout', this);
29910         
29911     },
29912     
29913     _resetLayout : function()
29914     {
29915         if(this.isHorizontal){
29916             this.horizontalMeasureColumns();
29917             return;
29918         }
29919         
29920         this.verticalMeasureColumns();
29921         
29922     },
29923     
29924     verticalMeasureColumns : function()
29925     {
29926         this.getContainerWidth();
29927         
29928 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29929 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29930 //            return;
29931 //        }
29932         
29933         var boxWidth = this.boxWidth + this.padWidth;
29934         
29935         if(this.containerWidth < this.boxWidth){
29936             boxWidth = this.containerWidth
29937         }
29938         
29939         var containerWidth = this.containerWidth;
29940         
29941         var cols = Math.floor(containerWidth / boxWidth);
29942         
29943         this.cols = Math.max( cols, 1 );
29944         
29945         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29946         
29947         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29948         
29949         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29950         
29951         this.colWidth = boxWidth + avail - this.padWidth;
29952         
29953         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29954         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29955     },
29956     
29957     horizontalMeasureColumns : function()
29958     {
29959         this.getContainerWidth();
29960         
29961         var boxWidth = this.boxWidth;
29962         
29963         if(this.containerWidth < boxWidth){
29964             boxWidth = this.containerWidth;
29965         }
29966         
29967         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29968         
29969         this.el.setHeight(boxWidth);
29970         
29971     },
29972     
29973     getContainerWidth : function()
29974     {
29975         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29976     },
29977     
29978     layoutItems : function( isInstant )
29979     {
29980         Roo.log(this.bricks);
29981         
29982         var items = Roo.apply([], this.bricks);
29983         
29984         if(this.isHorizontal){
29985             this._horizontalLayoutItems( items , isInstant );
29986             return;
29987         }
29988         
29989 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29990 //            this._verticalAlternativeLayoutItems( items , isInstant );
29991 //            return;
29992 //        }
29993         
29994         this._verticalLayoutItems( items , isInstant );
29995         
29996     },
29997     
29998     _verticalLayoutItems : function ( items , isInstant)
29999     {
30000         if ( !items || !items.length ) {
30001             return;
30002         }
30003         
30004         var standard = [
30005             ['xs', 'xs', 'xs', 'tall'],
30006             ['xs', 'xs', 'tall'],
30007             ['xs', 'xs', 'sm'],
30008             ['xs', 'xs', 'xs'],
30009             ['xs', 'tall'],
30010             ['xs', 'sm'],
30011             ['xs', 'xs'],
30012             ['xs'],
30013             
30014             ['sm', 'xs', 'xs'],
30015             ['sm', 'xs'],
30016             ['sm'],
30017             
30018             ['tall', 'xs', 'xs', 'xs'],
30019             ['tall', 'xs', 'xs'],
30020             ['tall', 'xs'],
30021             ['tall']
30022             
30023         ];
30024         
30025         var queue = [];
30026         
30027         var boxes = [];
30028         
30029         var box = [];
30030         
30031         Roo.each(items, function(item, k){
30032             
30033             switch (item.size) {
30034                 // these layouts take up a full box,
30035                 case 'md' :
30036                 case 'md-left' :
30037                 case 'md-right' :
30038                 case 'wide' :
30039                     
30040                     if(box.length){
30041                         boxes.push(box);
30042                         box = [];
30043                     }
30044                     
30045                     boxes.push([item]);
30046                     
30047                     break;
30048                     
30049                 case 'xs' :
30050                 case 'sm' :
30051                 case 'tall' :
30052                     
30053                     box.push(item);
30054                     
30055                     break;
30056                 default :
30057                     break;
30058                     
30059             }
30060             
30061         }, this);
30062         
30063         if(box.length){
30064             boxes.push(box);
30065             box = [];
30066         }
30067         
30068         var filterPattern = function(box, length)
30069         {
30070             if(!box.length){
30071                 return;
30072             }
30073             
30074             var match = false;
30075             
30076             var pattern = box.slice(0, length);
30077             
30078             var format = [];
30079             
30080             Roo.each(pattern, function(i){
30081                 format.push(i.size);
30082             }, this);
30083             
30084             Roo.each(standard, function(s){
30085                 
30086                 if(String(s) != String(format)){
30087                     return;
30088                 }
30089                 
30090                 match = true;
30091                 return false;
30092                 
30093             }, this);
30094             
30095             if(!match && length == 1){
30096                 return;
30097             }
30098             
30099             if(!match){
30100                 filterPattern(box, length - 1);
30101                 return;
30102             }
30103                 
30104             queue.push(pattern);
30105
30106             box = box.slice(length, box.length);
30107
30108             filterPattern(box, 4);
30109
30110             return;
30111             
30112         }
30113         
30114         Roo.each(boxes, function(box, k){
30115             
30116             if(!box.length){
30117                 return;
30118             }
30119             
30120             if(box.length == 1){
30121                 queue.push(box);
30122                 return;
30123             }
30124             
30125             filterPattern(box, 4);
30126             
30127         }, this);
30128         
30129         this._processVerticalLayoutQueue( queue, isInstant );
30130         
30131     },
30132     
30133 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30134 //    {
30135 //        if ( !items || !items.length ) {
30136 //            return;
30137 //        }
30138 //
30139 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30140 //        
30141 //    },
30142     
30143     _horizontalLayoutItems : function ( items , isInstant)
30144     {
30145         if ( !items || !items.length || items.length < 3) {
30146             return;
30147         }
30148         
30149         items.reverse();
30150         
30151         var eItems = items.slice(0, 3);
30152         
30153         items = items.slice(3, items.length);
30154         
30155         var standard = [
30156             ['xs', 'xs', 'xs', 'wide'],
30157             ['xs', 'xs', 'wide'],
30158             ['xs', 'xs', 'sm'],
30159             ['xs', 'xs', 'xs'],
30160             ['xs', 'wide'],
30161             ['xs', 'sm'],
30162             ['xs', 'xs'],
30163             ['xs'],
30164             
30165             ['sm', 'xs', 'xs'],
30166             ['sm', 'xs'],
30167             ['sm'],
30168             
30169             ['wide', 'xs', 'xs', 'xs'],
30170             ['wide', 'xs', 'xs'],
30171             ['wide', 'xs'],
30172             ['wide'],
30173             
30174             ['wide-thin']
30175         ];
30176         
30177         var queue = [];
30178         
30179         var boxes = [];
30180         
30181         var box = [];
30182         
30183         Roo.each(items, function(item, k){
30184             
30185             switch (item.size) {
30186                 case 'md' :
30187                 case 'md-left' :
30188                 case 'md-right' :
30189                 case 'tall' :
30190                     
30191                     if(box.length){
30192                         boxes.push(box);
30193                         box = [];
30194                     }
30195                     
30196                     boxes.push([item]);
30197                     
30198                     break;
30199                     
30200                 case 'xs' :
30201                 case 'sm' :
30202                 case 'wide' :
30203                 case 'wide-thin' :
30204                     
30205                     box.push(item);
30206                     
30207                     break;
30208                 default :
30209                     break;
30210                     
30211             }
30212             
30213         }, this);
30214         
30215         if(box.length){
30216             boxes.push(box);
30217             box = [];
30218         }
30219         
30220         var filterPattern = function(box, length)
30221         {
30222             if(!box.length){
30223                 return;
30224             }
30225             
30226             var match = false;
30227             
30228             var pattern = box.slice(0, length);
30229             
30230             var format = [];
30231             
30232             Roo.each(pattern, function(i){
30233                 format.push(i.size);
30234             }, this);
30235             
30236             Roo.each(standard, function(s){
30237                 
30238                 if(String(s) != String(format)){
30239                     return;
30240                 }
30241                 
30242                 match = true;
30243                 return false;
30244                 
30245             }, this);
30246             
30247             if(!match && length == 1){
30248                 return;
30249             }
30250             
30251             if(!match){
30252                 filterPattern(box, length - 1);
30253                 return;
30254             }
30255                 
30256             queue.push(pattern);
30257
30258             box = box.slice(length, box.length);
30259
30260             filterPattern(box, 4);
30261
30262             return;
30263             
30264         }
30265         
30266         Roo.each(boxes, function(box, k){
30267             
30268             if(!box.length){
30269                 return;
30270             }
30271             
30272             if(box.length == 1){
30273                 queue.push(box);
30274                 return;
30275             }
30276             
30277             filterPattern(box, 4);
30278             
30279         }, this);
30280         
30281         
30282         var prune = [];
30283         
30284         var pos = this.el.getBox(true);
30285         
30286         var minX = pos.x;
30287         
30288         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30289         
30290         var hit_end = false;
30291         
30292         Roo.each(queue, function(box){
30293             
30294             if(hit_end){
30295                 
30296                 Roo.each(box, function(b){
30297                 
30298                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30299                     b.el.hide();
30300
30301                 }, this);
30302
30303                 return;
30304             }
30305             
30306             var mx = 0;
30307             
30308             Roo.each(box, function(b){
30309                 
30310                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30311                 b.el.show();
30312
30313                 mx = Math.max(mx, b.x);
30314                 
30315             }, this);
30316             
30317             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30318             
30319             if(maxX < minX){
30320                 
30321                 Roo.each(box, function(b){
30322                 
30323                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30324                     b.el.hide();
30325                     
30326                 }, this);
30327                 
30328                 hit_end = true;
30329                 
30330                 return;
30331             }
30332             
30333             prune.push(box);
30334             
30335         }, this);
30336         
30337         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30338     },
30339     
30340     /** Sets position of item in DOM
30341     * @param {Element} item
30342     * @param {Number} x - horizontal position
30343     * @param {Number} y - vertical position
30344     * @param {Boolean} isInstant - disables transitions
30345     */
30346     _processVerticalLayoutQueue : function( queue, isInstant )
30347     {
30348         var pos = this.el.getBox(true);
30349         var x = pos.x;
30350         var y = pos.y;
30351         var maxY = [];
30352         
30353         for (var i = 0; i < this.cols; i++){
30354             maxY[i] = pos.y;
30355         }
30356         
30357         Roo.each(queue, function(box, k){
30358             
30359             var col = k % this.cols;
30360             
30361             Roo.each(box, function(b,kk){
30362                 
30363                 b.el.position('absolute');
30364                 
30365                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30366                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30367                 
30368                 if(b.size == 'md-left' || b.size == 'md-right'){
30369                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30370                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30371                 }
30372                 
30373                 b.el.setWidth(width);
30374                 b.el.setHeight(height);
30375                 // iframe?
30376                 b.el.select('iframe',true).setSize(width,height);
30377                 
30378             }, this);
30379             
30380             for (var i = 0; i < this.cols; i++){
30381                 
30382                 if(maxY[i] < maxY[col]){
30383                     col = i;
30384                     continue;
30385                 }
30386                 
30387                 col = Math.min(col, i);
30388                 
30389             }
30390             
30391             x = pos.x + col * (this.colWidth + this.padWidth);
30392             
30393             y = maxY[col];
30394             
30395             var positions = [];
30396             
30397             switch (box.length){
30398                 case 1 :
30399                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30400                     break;
30401                 case 2 :
30402                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30403                     break;
30404                 case 3 :
30405                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30406                     break;
30407                 case 4 :
30408                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30409                     break;
30410                 default :
30411                     break;
30412             }
30413             
30414             Roo.each(box, function(b,kk){
30415                 
30416                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30417                 
30418                 var sz = b.el.getSize();
30419                 
30420                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30421                 
30422             }, this);
30423             
30424         }, this);
30425         
30426         var mY = 0;
30427         
30428         for (var i = 0; i < this.cols; i++){
30429             mY = Math.max(mY, maxY[i]);
30430         }
30431         
30432         this.el.setHeight(mY - pos.y);
30433         
30434     },
30435     
30436 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30437 //    {
30438 //        var pos = this.el.getBox(true);
30439 //        var x = pos.x;
30440 //        var y = pos.y;
30441 //        var maxX = pos.right;
30442 //        
30443 //        var maxHeight = 0;
30444 //        
30445 //        Roo.each(items, function(item, k){
30446 //            
30447 //            var c = k % 2;
30448 //            
30449 //            item.el.position('absolute');
30450 //                
30451 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30452 //
30453 //            item.el.setWidth(width);
30454 //
30455 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30456 //
30457 //            item.el.setHeight(height);
30458 //            
30459 //            if(c == 0){
30460 //                item.el.setXY([x, y], isInstant ? false : true);
30461 //            } else {
30462 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30463 //            }
30464 //            
30465 //            y = y + height + this.alternativePadWidth;
30466 //            
30467 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30468 //            
30469 //        }, this);
30470 //        
30471 //        this.el.setHeight(maxHeight);
30472 //        
30473 //    },
30474     
30475     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30476     {
30477         var pos = this.el.getBox(true);
30478         
30479         var minX = pos.x;
30480         var minY = pos.y;
30481         
30482         var maxX = pos.right;
30483         
30484         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30485         
30486         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30487         
30488         Roo.each(queue, function(box, k){
30489             
30490             Roo.each(box, function(b, kk){
30491                 
30492                 b.el.position('absolute');
30493                 
30494                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30495                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30496                 
30497                 if(b.size == 'md-left' || b.size == 'md-right'){
30498                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30499                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30500                 }
30501                 
30502                 b.el.setWidth(width);
30503                 b.el.setHeight(height);
30504                 
30505             }, this);
30506             
30507             if(!box.length){
30508                 return;
30509             }
30510             
30511             var positions = [];
30512             
30513             switch (box.length){
30514                 case 1 :
30515                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30516                     break;
30517                 case 2 :
30518                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30519                     break;
30520                 case 3 :
30521                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30522                     break;
30523                 case 4 :
30524                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30525                     break;
30526                 default :
30527                     break;
30528             }
30529             
30530             Roo.each(box, function(b,kk){
30531                 
30532                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30533                 
30534                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30535                 
30536             }, this);
30537             
30538         }, this);
30539         
30540     },
30541     
30542     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30543     {
30544         Roo.each(eItems, function(b,k){
30545             
30546             b.size = (k == 0) ? 'sm' : 'xs';
30547             b.x = (k == 0) ? 2 : 1;
30548             b.y = (k == 0) ? 2 : 1;
30549             
30550             b.el.position('absolute');
30551             
30552             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30553                 
30554             b.el.setWidth(width);
30555             
30556             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30557             
30558             b.el.setHeight(height);
30559             
30560         }, this);
30561
30562         var positions = [];
30563         
30564         positions.push({
30565             x : maxX - this.unitWidth * 2 - this.gutter,
30566             y : minY
30567         });
30568         
30569         positions.push({
30570             x : maxX - this.unitWidth,
30571             y : minY + (this.unitWidth + this.gutter) * 2
30572         });
30573         
30574         positions.push({
30575             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30576             y : minY
30577         });
30578         
30579         Roo.each(eItems, function(b,k){
30580             
30581             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30582
30583         }, this);
30584         
30585     },
30586     
30587     getVerticalOneBoxColPositions : function(x, y, box)
30588     {
30589         var pos = [];
30590         
30591         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30592         
30593         if(box[0].size == 'md-left'){
30594             rand = 0;
30595         }
30596         
30597         if(box[0].size == 'md-right'){
30598             rand = 1;
30599         }
30600         
30601         pos.push({
30602             x : x + (this.unitWidth + this.gutter) * rand,
30603             y : y
30604         });
30605         
30606         return pos;
30607     },
30608     
30609     getVerticalTwoBoxColPositions : function(x, y, box)
30610     {
30611         var pos = [];
30612         
30613         if(box[0].size == 'xs'){
30614             
30615             pos.push({
30616                 x : x,
30617                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30618             });
30619
30620             pos.push({
30621                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30622                 y : y
30623             });
30624             
30625             return pos;
30626             
30627         }
30628         
30629         pos.push({
30630             x : x,
30631             y : y
30632         });
30633
30634         pos.push({
30635             x : x + (this.unitWidth + this.gutter) * 2,
30636             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30637         });
30638         
30639         return pos;
30640         
30641     },
30642     
30643     getVerticalThreeBoxColPositions : function(x, y, box)
30644     {
30645         var pos = [];
30646         
30647         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30648             
30649             pos.push({
30650                 x : x,
30651                 y : y
30652             });
30653
30654             pos.push({
30655                 x : x + (this.unitWidth + this.gutter) * 1,
30656                 y : y
30657             });
30658             
30659             pos.push({
30660                 x : x + (this.unitWidth + this.gutter) * 2,
30661                 y : y
30662             });
30663             
30664             return pos;
30665             
30666         }
30667         
30668         if(box[0].size == 'xs' && box[1].size == 'xs'){
30669             
30670             pos.push({
30671                 x : x,
30672                 y : y
30673             });
30674
30675             pos.push({
30676                 x : x,
30677                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30678             });
30679             
30680             pos.push({
30681                 x : x + (this.unitWidth + this.gutter) * 1,
30682                 y : y
30683             });
30684             
30685             return pos;
30686             
30687         }
30688         
30689         pos.push({
30690             x : x,
30691             y : y
30692         });
30693
30694         pos.push({
30695             x : x + (this.unitWidth + this.gutter) * 2,
30696             y : y
30697         });
30698
30699         pos.push({
30700             x : x + (this.unitWidth + this.gutter) * 2,
30701             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30702         });
30703             
30704         return pos;
30705         
30706     },
30707     
30708     getVerticalFourBoxColPositions : function(x, y, box)
30709     {
30710         var pos = [];
30711         
30712         if(box[0].size == 'xs'){
30713             
30714             pos.push({
30715                 x : x,
30716                 y : y
30717             });
30718
30719             pos.push({
30720                 x : x,
30721                 y : y + (this.unitHeight + this.gutter) * 1
30722             });
30723             
30724             pos.push({
30725                 x : x,
30726                 y : y + (this.unitHeight + this.gutter) * 2
30727             });
30728             
30729             pos.push({
30730                 x : x + (this.unitWidth + this.gutter) * 1,
30731                 y : y
30732             });
30733             
30734             return pos;
30735             
30736         }
30737         
30738         pos.push({
30739             x : x,
30740             y : y
30741         });
30742
30743         pos.push({
30744             x : x + (this.unitWidth + this.gutter) * 2,
30745             y : y
30746         });
30747
30748         pos.push({
30749             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30750             y : y + (this.unitHeight + this.gutter) * 1
30751         });
30752
30753         pos.push({
30754             x : x + (this.unitWidth + this.gutter) * 2,
30755             y : y + (this.unitWidth + this.gutter) * 2
30756         });
30757
30758         return pos;
30759         
30760     },
30761     
30762     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30763     {
30764         var pos = [];
30765         
30766         if(box[0].size == 'md-left'){
30767             pos.push({
30768                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30769                 y : minY
30770             });
30771             
30772             return pos;
30773         }
30774         
30775         if(box[0].size == 'md-right'){
30776             pos.push({
30777                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30778                 y : minY + (this.unitWidth + this.gutter) * 1
30779             });
30780             
30781             return pos;
30782         }
30783         
30784         var rand = Math.floor(Math.random() * (4 - box[0].y));
30785         
30786         pos.push({
30787             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30788             y : minY + (this.unitWidth + this.gutter) * rand
30789         });
30790         
30791         return pos;
30792         
30793     },
30794     
30795     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30796     {
30797         var pos = [];
30798         
30799         if(box[0].size == 'xs'){
30800             
30801             pos.push({
30802                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30803                 y : minY
30804             });
30805
30806             pos.push({
30807                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30808                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30809             });
30810             
30811             return pos;
30812             
30813         }
30814         
30815         pos.push({
30816             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30817             y : minY
30818         });
30819
30820         pos.push({
30821             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30822             y : minY + (this.unitWidth + this.gutter) * 2
30823         });
30824         
30825         return pos;
30826         
30827     },
30828     
30829     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30830     {
30831         var pos = [];
30832         
30833         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30834             
30835             pos.push({
30836                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30837                 y : minY
30838             });
30839
30840             pos.push({
30841                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30842                 y : minY + (this.unitWidth + this.gutter) * 1
30843             });
30844             
30845             pos.push({
30846                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30847                 y : minY + (this.unitWidth + this.gutter) * 2
30848             });
30849             
30850             return pos;
30851             
30852         }
30853         
30854         if(box[0].size == 'xs' && box[1].size == 'xs'){
30855             
30856             pos.push({
30857                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30858                 y : minY
30859             });
30860
30861             pos.push({
30862                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30863                 y : minY
30864             });
30865             
30866             pos.push({
30867                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30868                 y : minY + (this.unitWidth + this.gutter) * 1
30869             });
30870             
30871             return pos;
30872             
30873         }
30874         
30875         pos.push({
30876             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30877             y : minY
30878         });
30879
30880         pos.push({
30881             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30882             y : minY + (this.unitWidth + this.gutter) * 2
30883         });
30884
30885         pos.push({
30886             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30887             y : minY + (this.unitWidth + this.gutter) * 2
30888         });
30889             
30890         return pos;
30891         
30892     },
30893     
30894     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30895     {
30896         var pos = [];
30897         
30898         if(box[0].size == 'xs'){
30899             
30900             pos.push({
30901                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30902                 y : minY
30903             });
30904
30905             pos.push({
30906                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30907                 y : minY
30908             });
30909             
30910             pos.push({
30911                 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),
30912                 y : minY
30913             });
30914             
30915             pos.push({
30916                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30917                 y : minY + (this.unitWidth + this.gutter) * 1
30918             });
30919             
30920             return pos;
30921             
30922         }
30923         
30924         pos.push({
30925             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30926             y : minY
30927         });
30928         
30929         pos.push({
30930             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30931             y : minY + (this.unitWidth + this.gutter) * 2
30932         });
30933         
30934         pos.push({
30935             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30936             y : minY + (this.unitWidth + this.gutter) * 2
30937         });
30938         
30939         pos.push({
30940             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),
30941             y : minY + (this.unitWidth + this.gutter) * 2
30942         });
30943
30944         return pos;
30945         
30946     }
30947     
30948 });
30949
30950  
30951
30952  /**
30953  *
30954  * This is based on 
30955  * http://masonry.desandro.com
30956  *
30957  * The idea is to render all the bricks based on vertical width...
30958  *
30959  * The original code extends 'outlayer' - we might need to use that....
30960  * 
30961  */
30962
30963
30964 /**
30965  * @class Roo.bootstrap.LayoutMasonryAuto
30966  * @extends Roo.bootstrap.Component
30967  * Bootstrap Layout Masonry class
30968  * 
30969  * @constructor
30970  * Create a new Element
30971  * @param {Object} config The config object
30972  */
30973
30974 Roo.bootstrap.LayoutMasonryAuto = function(config){
30975     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30976 };
30977
30978 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30979     
30980       /**
30981      * @cfg {Boolean} isFitWidth  - resize the width..
30982      */   
30983     isFitWidth : false,  // options..
30984     /**
30985      * @cfg {Boolean} isOriginLeft = left align?
30986      */   
30987     isOriginLeft : true,
30988     /**
30989      * @cfg {Boolean} isOriginTop = top align?
30990      */   
30991     isOriginTop : false,
30992     /**
30993      * @cfg {Boolean} isLayoutInstant = no animation?
30994      */   
30995     isLayoutInstant : false, // needed?
30996     /**
30997      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30998      */   
30999     isResizingContainer : true,
31000     /**
31001      * @cfg {Number} columnWidth  width of the columns 
31002      */   
31003     
31004     columnWidth : 0,
31005     
31006     /**
31007      * @cfg {Number} maxCols maximum number of columns
31008      */   
31009     
31010     maxCols: 0,
31011     /**
31012      * @cfg {Number} padHeight padding below box..
31013      */   
31014     
31015     padHeight : 10, 
31016     
31017     /**
31018      * @cfg {Boolean} isAutoInitial defalut true
31019      */   
31020     
31021     isAutoInitial : true, 
31022     
31023     // private?
31024     gutter : 0,
31025     
31026     containerWidth: 0,
31027     initialColumnWidth : 0,
31028     currentSize : null,
31029     
31030     colYs : null, // array.
31031     maxY : 0,
31032     padWidth: 10,
31033     
31034     
31035     tag: 'div',
31036     cls: '',
31037     bricks: null, //CompositeElement
31038     cols : 0, // array?
31039     // element : null, // wrapped now this.el
31040     _isLayoutInited : null, 
31041     
31042     
31043     getAutoCreate : function(){
31044         
31045         var cfg = {
31046             tag: this.tag,
31047             cls: 'blog-masonary-wrapper ' + this.cls,
31048             cn : {
31049                 cls : 'mas-boxes masonary'
31050             }
31051         };
31052         
31053         return cfg;
31054     },
31055     
31056     getChildContainer: function( )
31057     {
31058         if (this.boxesEl) {
31059             return this.boxesEl;
31060         }
31061         
31062         this.boxesEl = this.el.select('.mas-boxes').first();
31063         
31064         return this.boxesEl;
31065     },
31066     
31067     
31068     initEvents : function()
31069     {
31070         var _this = this;
31071         
31072         if(this.isAutoInitial){
31073             Roo.log('hook children rendered');
31074             this.on('childrenrendered', function() {
31075                 Roo.log('children rendered');
31076                 _this.initial();
31077             } ,this);
31078         }
31079         
31080     },
31081     
31082     initial : function()
31083     {
31084         this.reloadItems();
31085
31086         this.currentSize = this.el.getBox(true);
31087
31088         /// was window resize... - let's see if this works..
31089         Roo.EventManager.onWindowResize(this.resize, this); 
31090
31091         if(!this.isAutoInitial){
31092             this.layout();
31093             return;
31094         }
31095         
31096         this.layout.defer(500,this);
31097     },
31098     
31099     reloadItems: function()
31100     {
31101         this.bricks = this.el.select('.masonry-brick', true);
31102         
31103         this.bricks.each(function(b) {
31104             //Roo.log(b.getSize());
31105             if (!b.attr('originalwidth')) {
31106                 b.attr('originalwidth',  b.getSize().width);
31107             }
31108             
31109         });
31110         
31111         Roo.log(this.bricks.elements.length);
31112     },
31113     
31114     resize : function()
31115     {
31116         Roo.log('resize');
31117         var cs = this.el.getBox(true);
31118         
31119         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31120             Roo.log("no change in with or X");
31121             return;
31122         }
31123         this.currentSize = cs;
31124         this.layout();
31125     },
31126     
31127     layout : function()
31128     {
31129          Roo.log('layout');
31130         this._resetLayout();
31131         //this._manageStamps();
31132       
31133         // don't animate first layout
31134         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31135         this.layoutItems( isInstant );
31136       
31137         // flag for initalized
31138         this._isLayoutInited = true;
31139     },
31140     
31141     layoutItems : function( isInstant )
31142     {
31143         //var items = this._getItemsForLayout( this.items );
31144         // original code supports filtering layout items.. we just ignore it..
31145         
31146         this._layoutItems( this.bricks , isInstant );
31147       
31148         this._postLayout();
31149     },
31150     _layoutItems : function ( items , isInstant)
31151     {
31152        //this.fireEvent( 'layout', this, items );
31153     
31154
31155         if ( !items || !items.elements.length ) {
31156           // no items, emit event with empty array
31157             return;
31158         }
31159
31160         var queue = [];
31161         items.each(function(item) {
31162             Roo.log("layout item");
31163             Roo.log(item);
31164             // get x/y object from method
31165             var position = this._getItemLayoutPosition( item );
31166             // enqueue
31167             position.item = item;
31168             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31169             queue.push( position );
31170         }, this);
31171       
31172         this._processLayoutQueue( queue );
31173     },
31174     /** Sets position of item in DOM
31175     * @param {Element} item
31176     * @param {Number} x - horizontal position
31177     * @param {Number} y - vertical position
31178     * @param {Boolean} isInstant - disables transitions
31179     */
31180     _processLayoutQueue : function( queue )
31181     {
31182         for ( var i=0, len = queue.length; i < len; i++ ) {
31183             var obj = queue[i];
31184             obj.item.position('absolute');
31185             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31186         }
31187     },
31188       
31189     
31190     /**
31191     * Any logic you want to do after each layout,
31192     * i.e. size the container
31193     */
31194     _postLayout : function()
31195     {
31196         this.resizeContainer();
31197     },
31198     
31199     resizeContainer : function()
31200     {
31201         if ( !this.isResizingContainer ) {
31202             return;
31203         }
31204         var size = this._getContainerSize();
31205         if ( size ) {
31206             this.el.setSize(size.width,size.height);
31207             this.boxesEl.setSize(size.width,size.height);
31208         }
31209     },
31210     
31211     
31212     
31213     _resetLayout : function()
31214     {
31215         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31216         this.colWidth = this.el.getWidth();
31217         //this.gutter = this.el.getWidth(); 
31218         
31219         this.measureColumns();
31220
31221         // reset column Y
31222         var i = this.cols;
31223         this.colYs = [];
31224         while (i--) {
31225             this.colYs.push( 0 );
31226         }
31227     
31228         this.maxY = 0;
31229     },
31230
31231     measureColumns : function()
31232     {
31233         this.getContainerWidth();
31234       // if columnWidth is 0, default to outerWidth of first item
31235         if ( !this.columnWidth ) {
31236             var firstItem = this.bricks.first();
31237             Roo.log(firstItem);
31238             this.columnWidth  = this.containerWidth;
31239             if (firstItem && firstItem.attr('originalwidth') ) {
31240                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31241             }
31242             // columnWidth fall back to item of first element
31243             Roo.log("set column width?");
31244                         this.initialColumnWidth = this.columnWidth  ;
31245
31246             // if first elem has no width, default to size of container
31247             
31248         }
31249         
31250         
31251         if (this.initialColumnWidth) {
31252             this.columnWidth = this.initialColumnWidth;
31253         }
31254         
31255         
31256             
31257         // column width is fixed at the top - however if container width get's smaller we should
31258         // reduce it...
31259         
31260         // this bit calcs how man columns..
31261             
31262         var columnWidth = this.columnWidth += this.gutter;
31263       
31264         // calculate columns
31265         var containerWidth = this.containerWidth + this.gutter;
31266         
31267         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31268         // fix rounding errors, typically with gutters
31269         var excess = columnWidth - containerWidth % columnWidth;
31270         
31271         
31272         // if overshoot is less than a pixel, round up, otherwise floor it
31273         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31274         cols = Math[ mathMethod ]( cols );
31275         this.cols = Math.max( cols, 1 );
31276         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31277         
31278          // padding positioning..
31279         var totalColWidth = this.cols * this.columnWidth;
31280         var padavail = this.containerWidth - totalColWidth;
31281         // so for 2 columns - we need 3 'pads'
31282         
31283         var padNeeded = (1+this.cols) * this.padWidth;
31284         
31285         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31286         
31287         this.columnWidth += padExtra
31288         //this.padWidth = Math.floor(padavail /  ( this.cols));
31289         
31290         // adjust colum width so that padding is fixed??
31291         
31292         // we have 3 columns ... total = width * 3
31293         // we have X left over... that should be used by 
31294         
31295         //if (this.expandC) {
31296             
31297         //}
31298         
31299         
31300         
31301     },
31302     
31303     getContainerWidth : function()
31304     {
31305        /* // container is parent if fit width
31306         var container = this.isFitWidth ? this.element.parentNode : this.element;
31307         // check that this.size and size are there
31308         // IE8 triggers resize on body size change, so they might not be
31309         
31310         var size = getSize( container );  //FIXME
31311         this.containerWidth = size && size.innerWidth; //FIXME
31312         */
31313          
31314         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31315         
31316     },
31317     
31318     _getItemLayoutPosition : function( item )  // what is item?
31319     {
31320         // we resize the item to our columnWidth..
31321       
31322         item.setWidth(this.columnWidth);
31323         item.autoBoxAdjust  = false;
31324         
31325         var sz = item.getSize();
31326  
31327         // how many columns does this brick span
31328         var remainder = this.containerWidth % this.columnWidth;
31329         
31330         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31331         // round if off by 1 pixel, otherwise use ceil
31332         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31333         colSpan = Math.min( colSpan, this.cols );
31334         
31335         // normally this should be '1' as we dont' currently allow multi width columns..
31336         
31337         var colGroup = this._getColGroup( colSpan );
31338         // get the minimum Y value from the columns
31339         var minimumY = Math.min.apply( Math, colGroup );
31340         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31341         
31342         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31343          
31344         // position the brick
31345         var position = {
31346             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31347             y: this.currentSize.y + minimumY + this.padHeight
31348         };
31349         
31350         Roo.log(position);
31351         // apply setHeight to necessary columns
31352         var setHeight = minimumY + sz.height + this.padHeight;
31353         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31354         
31355         var setSpan = this.cols + 1 - colGroup.length;
31356         for ( var i = 0; i < setSpan; i++ ) {
31357           this.colYs[ shortColIndex + i ] = setHeight ;
31358         }
31359       
31360         return position;
31361     },
31362     
31363     /**
31364      * @param {Number} colSpan - number of columns the element spans
31365      * @returns {Array} colGroup
31366      */
31367     _getColGroup : function( colSpan )
31368     {
31369         if ( colSpan < 2 ) {
31370           // if brick spans only one column, use all the column Ys
31371           return this.colYs;
31372         }
31373       
31374         var colGroup = [];
31375         // how many different places could this brick fit horizontally
31376         var groupCount = this.cols + 1 - colSpan;
31377         // for each group potential horizontal position
31378         for ( var i = 0; i < groupCount; i++ ) {
31379           // make an array of colY values for that one group
31380           var groupColYs = this.colYs.slice( i, i + colSpan );
31381           // and get the max value of the array
31382           colGroup[i] = Math.max.apply( Math, groupColYs );
31383         }
31384         return colGroup;
31385     },
31386     /*
31387     _manageStamp : function( stamp )
31388     {
31389         var stampSize =  stamp.getSize();
31390         var offset = stamp.getBox();
31391         // get the columns that this stamp affects
31392         var firstX = this.isOriginLeft ? offset.x : offset.right;
31393         var lastX = firstX + stampSize.width;
31394         var firstCol = Math.floor( firstX / this.columnWidth );
31395         firstCol = Math.max( 0, firstCol );
31396         
31397         var lastCol = Math.floor( lastX / this.columnWidth );
31398         // lastCol should not go over if multiple of columnWidth #425
31399         lastCol -= lastX % this.columnWidth ? 0 : 1;
31400         lastCol = Math.min( this.cols - 1, lastCol );
31401         
31402         // set colYs to bottom of the stamp
31403         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31404             stampSize.height;
31405             
31406         for ( var i = firstCol; i <= lastCol; i++ ) {
31407           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31408         }
31409     },
31410     */
31411     
31412     _getContainerSize : function()
31413     {
31414         this.maxY = Math.max.apply( Math, this.colYs );
31415         var size = {
31416             height: this.maxY
31417         };
31418       
31419         if ( this.isFitWidth ) {
31420             size.width = this._getContainerFitWidth();
31421         }
31422       
31423         return size;
31424     },
31425     
31426     _getContainerFitWidth : function()
31427     {
31428         var unusedCols = 0;
31429         // count unused columns
31430         var i = this.cols;
31431         while ( --i ) {
31432           if ( this.colYs[i] !== 0 ) {
31433             break;
31434           }
31435           unusedCols++;
31436         }
31437         // fit container to columns that have been used
31438         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31439     },
31440     
31441     needsResizeLayout : function()
31442     {
31443         var previousWidth = this.containerWidth;
31444         this.getContainerWidth();
31445         return previousWidth !== this.containerWidth;
31446     }
31447  
31448 });
31449
31450  
31451
31452  /*
31453  * - LGPL
31454  *
31455  * element
31456  * 
31457  */
31458
31459 /**
31460  * @class Roo.bootstrap.MasonryBrick
31461  * @extends Roo.bootstrap.Component
31462  * Bootstrap MasonryBrick class
31463  * 
31464  * @constructor
31465  * Create a new MasonryBrick
31466  * @param {Object} config The config object
31467  */
31468
31469 Roo.bootstrap.MasonryBrick = function(config){
31470     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31471     
31472     this.addEvents({
31473         // raw events
31474         /**
31475          * @event click
31476          * When a MasonryBrick is clcik
31477          * @param {Roo.bootstrap.MasonryBrick} this
31478          * @param {Roo.EventObject} e
31479          */
31480         "click" : true
31481     });
31482 };
31483
31484 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31485     
31486     /**
31487      * @cfg {String} title
31488      */   
31489     title : '',
31490     /**
31491      * @cfg {String} html
31492      */   
31493     html : '',
31494     /**
31495      * @cfg {String} bgimage
31496      */   
31497     bgimage : '',
31498     /**
31499      * @cfg {String} videourl
31500      */   
31501     videourl : '',
31502     /**
31503      * @cfg {String} cls
31504      */   
31505     cls : '',
31506     /**
31507      * @cfg {String} href
31508      */   
31509     href : '',
31510     /**
31511      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31512      */   
31513     size : 'xs',
31514     
31515     /**
31516      * @cfg {String} placetitle (center|bottom)
31517      */   
31518     placetitle : '',
31519     
31520     /**
31521      * @cfg {Boolean} isFitContainer defalut true
31522      */   
31523     isFitContainer : true, 
31524     
31525     /**
31526      * @cfg {Boolean} preventDefault defalut false
31527      */   
31528     preventDefault : false, 
31529     
31530     /**
31531      * @cfg {Boolean} inverse defalut false
31532      */   
31533     maskInverse : false, 
31534     
31535     getAutoCreate : function()
31536     {
31537         if(!this.isFitContainer){
31538             return this.getSplitAutoCreate();
31539         }
31540         
31541         var cls = 'masonry-brick masonry-brick-full';
31542         
31543         if(this.href.length){
31544             cls += ' masonry-brick-link';
31545         }
31546         
31547         if(this.bgimage.length){
31548             cls += ' masonry-brick-image';
31549         }
31550         
31551         if(this.maskInverse){
31552             cls += ' mask-inverse';
31553         }
31554         
31555         if(!this.html.length && !this.maskInverse){
31556             cls += ' enable-mask';
31557         }
31558         
31559         if(this.size){
31560             cls += ' masonry-' + this.size + '-brick';
31561         }
31562         
31563         if(this.placetitle.length){
31564             
31565             switch (this.placetitle) {
31566                 case 'center' :
31567                     cls += ' masonry-center-title';
31568                     break;
31569                 case 'bottom' :
31570                     cls += ' masonry-bottom-title';
31571                     break;
31572                 default:
31573                     break;
31574             }
31575             
31576         } else {
31577             if(!this.html.length && !this.bgimage.length){
31578                 cls += ' masonry-center-title';
31579             }
31580
31581             if(!this.html.length && this.bgimage.length){
31582                 cls += ' masonry-bottom-title';
31583             }
31584         }
31585         
31586         if(this.cls){
31587             cls += ' ' + this.cls;
31588         }
31589         
31590         var cfg = {
31591             tag: (this.href.length) ? 'a' : 'div',
31592             cls: cls,
31593             cn: [
31594                 {
31595                     tag: 'div',
31596                     cls: 'masonry-brick-paragraph',
31597                     cn: []
31598                 }
31599             ]
31600         };
31601         
31602         if(this.href.length){
31603             cfg.href = this.href;
31604         }
31605         
31606         var cn = cfg.cn[0].cn;
31607         
31608         if(this.title.length){
31609             cn.push({
31610                 tag: 'h4',
31611                 cls: 'masonry-brick-title',
31612                 html: this.title
31613             });
31614         }
31615         
31616         if(this.html.length){
31617             cn.push({
31618                 tag: 'p',
31619                 cls: 'masonry-brick-text',
31620                 html: this.html
31621             });
31622         }  
31623         if (!this.title.length && !this.html.length) {
31624             cfg.cn[0].cls += ' hide';
31625         }
31626         
31627         if(this.bgimage.length){
31628             cfg.cn.push({
31629                 tag: 'img',
31630                 cls: 'masonry-brick-image-view',
31631                 src: this.bgimage
31632             });
31633         }
31634         
31635         if(this.videourl.length){
31636             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31637             // youtube support only?
31638             cfg.cn.push({
31639                 tag: 'iframe',
31640                 cls: 'masonry-brick-image-view',
31641                 src: vurl,
31642                 frameborder : 0,
31643                 allowfullscreen : true
31644             });
31645             
31646             
31647         }
31648         
31649         cfg.cn.push({
31650             tag: 'div',
31651             cls: 'masonry-brick-mask'
31652         });
31653         
31654         return cfg;
31655         
31656     },
31657     
31658     getSplitAutoCreate : function()
31659     {
31660         var cls = 'masonry-brick masonry-brick-split';
31661         
31662         if(this.href.length){
31663             cls += ' masonry-brick-link';
31664         }
31665         
31666         if(this.bgimage.length){
31667             cls += ' masonry-brick-image';
31668         }
31669         
31670         if(this.size){
31671             cls += ' masonry-' + this.size + '-brick';
31672         }
31673         
31674         switch (this.placetitle) {
31675             case 'center' :
31676                 cls += ' masonry-center-title';
31677                 break;
31678             case 'bottom' :
31679                 cls += ' masonry-bottom-title';
31680                 break;
31681             default:
31682                 if(!this.bgimage.length){
31683                     cls += ' masonry-center-title';
31684                 }
31685
31686                 if(this.bgimage.length){
31687                     cls += ' masonry-bottom-title';
31688                 }
31689                 break;
31690         }
31691         
31692         if(this.cls){
31693             cls += ' ' + this.cls;
31694         }
31695         
31696         var cfg = {
31697             tag: (this.href.length) ? 'a' : 'div',
31698             cls: cls,
31699             cn: [
31700                 {
31701                     tag: 'div',
31702                     cls: 'masonry-brick-split-head',
31703                     cn: [
31704                         {
31705                             tag: 'div',
31706                             cls: 'masonry-brick-paragraph',
31707                             cn: []
31708                         }
31709                     ]
31710                 },
31711                 {
31712                     tag: 'div',
31713                     cls: 'masonry-brick-split-body',
31714                     cn: []
31715                 }
31716             ]
31717         };
31718         
31719         if(this.href.length){
31720             cfg.href = this.href;
31721         }
31722         
31723         if(this.title.length){
31724             cfg.cn[0].cn[0].cn.push({
31725                 tag: 'h4',
31726                 cls: 'masonry-brick-title',
31727                 html: this.title
31728             });
31729         }
31730         
31731         if(this.html.length){
31732             cfg.cn[1].cn.push({
31733                 tag: 'p',
31734                 cls: 'masonry-brick-text',
31735                 html: this.html
31736             });
31737         }
31738
31739         if(this.bgimage.length){
31740             cfg.cn[0].cn.push({
31741                 tag: 'img',
31742                 cls: 'masonry-brick-image-view',
31743                 src: this.bgimage
31744             });
31745         }
31746         
31747         if(this.videourl.length){
31748             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31749             // youtube support only?
31750             cfg.cn[0].cn.cn.push({
31751                 tag: 'iframe',
31752                 cls: 'masonry-brick-image-view',
31753                 src: vurl,
31754                 frameborder : 0,
31755                 allowfullscreen : true
31756             });
31757         }
31758         
31759         return cfg;
31760     },
31761     
31762     initEvents: function() 
31763     {
31764         switch (this.size) {
31765             case 'xs' :
31766                 this.x = 1;
31767                 this.y = 1;
31768                 break;
31769             case 'sm' :
31770                 this.x = 2;
31771                 this.y = 2;
31772                 break;
31773             case 'md' :
31774             case 'md-left' :
31775             case 'md-right' :
31776                 this.x = 3;
31777                 this.y = 3;
31778                 break;
31779             case 'tall' :
31780                 this.x = 2;
31781                 this.y = 3;
31782                 break;
31783             case 'wide' :
31784                 this.x = 3;
31785                 this.y = 2;
31786                 break;
31787             case 'wide-thin' :
31788                 this.x = 3;
31789                 this.y = 1;
31790                 break;
31791                         
31792             default :
31793                 break;
31794         }
31795         
31796         if(Roo.isTouch){
31797             this.el.on('touchstart', this.onTouchStart, this);
31798             this.el.on('touchmove', this.onTouchMove, this);
31799             this.el.on('touchend', this.onTouchEnd, this);
31800             this.el.on('contextmenu', this.onContextMenu, this);
31801         } else {
31802             this.el.on('mouseenter'  ,this.enter, this);
31803             this.el.on('mouseleave', this.leave, this);
31804             this.el.on('click', this.onClick, this);
31805         }
31806         
31807         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31808             this.parent().bricks.push(this);   
31809         }
31810         
31811     },
31812     
31813     onClick: function(e, el)
31814     {
31815         var time = this.endTimer - this.startTimer;
31816         
31817         if(Roo.isTouch){
31818             if(time > 1000){
31819                 e.preventDefault();
31820                 return;
31821             }
31822         }
31823         
31824         if(!this.preventDefault){
31825             return;
31826         }
31827         
31828         e.preventDefault();
31829         this.fireEvent('click', this);
31830     },
31831     
31832     enter: function(e, el)
31833     {
31834         e.preventDefault();
31835         
31836         if(!this.isFitContainer || this.maskInverse){
31837             return;
31838         }
31839         
31840         if(this.bgimage.length && this.html.length){
31841             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31842         }
31843     },
31844     
31845     leave: function(e, el)
31846     {
31847         e.preventDefault();
31848         
31849         if(!this.isFitContainer || this.maskInverse){
31850             return;
31851         }
31852         
31853         if(this.bgimage.length && this.html.length){
31854             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31855         }
31856     },
31857     
31858     onTouchStart: function(e, el)
31859     {
31860 //        e.preventDefault();
31861         
31862         this.touchmoved = false;
31863         
31864         if(!this.isFitContainer){
31865             return;
31866         }
31867         
31868         if(!this.bgimage.length || !this.html.length){
31869             return;
31870         }
31871         
31872         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31873         
31874         this.timer = new Date().getTime();
31875         
31876     },
31877     
31878     onTouchMove: function(e, el)
31879     {
31880         this.touchmoved = true;
31881     },
31882     
31883     onContextMenu : function(e,el)
31884     {
31885         e.preventDefault();
31886         e.stopPropagation();
31887         return false;
31888     },
31889     
31890     onTouchEnd: function(e, el)
31891     {
31892 //        e.preventDefault();
31893         
31894         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31895         
31896             this.leave(e,el);
31897             
31898             return;
31899         }
31900         
31901         if(!this.bgimage.length || !this.html.length){
31902             
31903             if(this.href.length){
31904                 window.location.href = this.href;
31905             }
31906             
31907             return;
31908         }
31909         
31910         if(!this.isFitContainer){
31911             return;
31912         }
31913         
31914         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31915         
31916         window.location.href = this.href;
31917     }
31918     
31919 });
31920
31921  
31922
31923  /*
31924  * - LGPL
31925  *
31926  * element
31927  * 
31928  */
31929
31930 /**
31931  * @class Roo.bootstrap.Brick
31932  * @extends Roo.bootstrap.Component
31933  * Bootstrap Brick class
31934  * 
31935  * @constructor
31936  * Create a new Brick
31937  * @param {Object} config The config object
31938  */
31939
31940 Roo.bootstrap.Brick = function(config){
31941     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31942     
31943     this.addEvents({
31944         // raw events
31945         /**
31946          * @event click
31947          * When a Brick is click
31948          * @param {Roo.bootstrap.Brick} this
31949          * @param {Roo.EventObject} e
31950          */
31951         "click" : true
31952     });
31953 };
31954
31955 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31956     
31957     /**
31958      * @cfg {String} title
31959      */   
31960     title : '',
31961     /**
31962      * @cfg {String} html
31963      */   
31964     html : '',
31965     /**
31966      * @cfg {String} bgimage
31967      */   
31968     bgimage : '',
31969     /**
31970      * @cfg {String} cls
31971      */   
31972     cls : '',
31973     /**
31974      * @cfg {String} href
31975      */   
31976     href : '',
31977     /**
31978      * @cfg {String} video
31979      */   
31980     video : '',
31981     /**
31982      * @cfg {Boolean} square
31983      */   
31984     square : true,
31985     
31986     getAutoCreate : function()
31987     {
31988         var cls = 'roo-brick';
31989         
31990         if(this.href.length){
31991             cls += ' roo-brick-link';
31992         }
31993         
31994         if(this.bgimage.length){
31995             cls += ' roo-brick-image';
31996         }
31997         
31998         if(!this.html.length && !this.bgimage.length){
31999             cls += ' roo-brick-center-title';
32000         }
32001         
32002         if(!this.html.length && this.bgimage.length){
32003             cls += ' roo-brick-bottom-title';
32004         }
32005         
32006         if(this.cls){
32007             cls += ' ' + this.cls;
32008         }
32009         
32010         var cfg = {
32011             tag: (this.href.length) ? 'a' : 'div',
32012             cls: cls,
32013             cn: [
32014                 {
32015                     tag: 'div',
32016                     cls: 'roo-brick-paragraph',
32017                     cn: []
32018                 }
32019             ]
32020         };
32021         
32022         if(this.href.length){
32023             cfg.href = this.href;
32024         }
32025         
32026         var cn = cfg.cn[0].cn;
32027         
32028         if(this.title.length){
32029             cn.push({
32030                 tag: 'h4',
32031                 cls: 'roo-brick-title',
32032                 html: this.title
32033             });
32034         }
32035         
32036         if(this.html.length){
32037             cn.push({
32038                 tag: 'p',
32039                 cls: 'roo-brick-text',
32040                 html: this.html
32041             });
32042         } else {
32043             cn.cls += ' hide';
32044         }
32045         
32046         if(this.bgimage.length){
32047             cfg.cn.push({
32048                 tag: 'img',
32049                 cls: 'roo-brick-image-view',
32050                 src: this.bgimage
32051             });
32052         }
32053         
32054         return cfg;
32055     },
32056     
32057     initEvents: function() 
32058     {
32059         if(this.title.length || this.html.length){
32060             this.el.on('mouseenter'  ,this.enter, this);
32061             this.el.on('mouseleave', this.leave, this);
32062         }
32063         
32064         
32065         Roo.EventManager.onWindowResize(this.resize, this); 
32066         
32067         this.resize();
32068     },
32069     
32070     resize : function()
32071     {
32072         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32073         
32074         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32075         
32076         if(this.bgimage.length){
32077             var image = this.el.select('.roo-brick-image-view', true).first();
32078             image.setWidth(paragraph.getWidth());
32079             image.setHeight(paragraph.getWidth());
32080             
32081             this.el.setHeight(paragraph.getWidth());
32082             
32083         }
32084         
32085     },
32086     
32087     enter: function(e, el)
32088     {
32089         e.preventDefault();
32090         
32091         if(this.bgimage.length){
32092             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32093             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32094         }
32095     },
32096     
32097     leave: function(e, el)
32098     {
32099         e.preventDefault();
32100         
32101         if(this.bgimage.length){
32102             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32103             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32104         }
32105     }
32106     
32107 });
32108
32109  
32110
32111  /*
32112  * - LGPL
32113  *
32114  * Input
32115  * 
32116  */
32117
32118 /**
32119  * @class Roo.bootstrap.NumberField
32120  * @extends Roo.bootstrap.Input
32121  * Bootstrap NumberField class
32122  * 
32123  * 
32124  * 
32125  * 
32126  * @constructor
32127  * Create a new NumberField
32128  * @param {Object} config The config object
32129  */
32130
32131 Roo.bootstrap.NumberField = function(config){
32132     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32133 };
32134
32135 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32136     
32137     /**
32138      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32139      */
32140     allowDecimals : true,
32141     /**
32142      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32143      */
32144     decimalSeparator : ".",
32145     /**
32146      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32147      */
32148     decimalPrecision : 2,
32149     /**
32150      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32151      */
32152     allowNegative : true,
32153     /**
32154      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32155      */
32156     minValue : Number.NEGATIVE_INFINITY,
32157     /**
32158      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32159      */
32160     maxValue : Number.MAX_VALUE,
32161     /**
32162      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32163      */
32164     minText : "The minimum value for this field is {0}",
32165     /**
32166      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32167      */
32168     maxText : "The maximum value for this field is {0}",
32169     /**
32170      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32171      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32172      */
32173     nanText : "{0} is not a valid number",
32174     /**
32175      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32176      */
32177     castInt : true,
32178
32179     // private
32180     initEvents : function()
32181     {   
32182         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32183         
32184         var allowed = "0123456789";
32185         
32186         if(this.allowDecimals){
32187             allowed += this.decimalSeparator;
32188         }
32189         
32190         if(this.allowNegative){
32191             allowed += "-";
32192         }
32193         
32194         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32195         
32196         var keyPress = function(e){
32197             
32198             var k = e.getKey();
32199             
32200             var c = e.getCharCode();
32201             
32202             if(
32203                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32204                     allowed.indexOf(String.fromCharCode(c)) === -1
32205             ){
32206                 e.stopEvent();
32207                 return;
32208             }
32209             
32210             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32211                 return;
32212             }
32213             
32214             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32215                 e.stopEvent();
32216             }
32217         };
32218         
32219         this.el.on("keypress", keyPress, this);
32220     },
32221     
32222     validateValue : function(value)
32223     {
32224         
32225         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32226             return false;
32227         }
32228         
32229         var num = this.parseValue(value);
32230         
32231         if(isNaN(num)){
32232             this.markInvalid(String.format(this.nanText, value));
32233             return false;
32234         }
32235         
32236         if(num < this.minValue){
32237             this.markInvalid(String.format(this.minText, this.minValue));
32238             return false;
32239         }
32240         
32241         if(num > this.maxValue){
32242             this.markInvalid(String.format(this.maxText, this.maxValue));
32243             return false;
32244         }
32245         
32246         return true;
32247     },
32248
32249     getValue : function()
32250     {
32251         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32252     },
32253
32254     parseValue : function(value)
32255     {
32256         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32257         return isNaN(value) ? '' : value;
32258     },
32259
32260     fixPrecision : function(value)
32261     {
32262         var nan = isNaN(value);
32263         
32264         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32265             return nan ? '' : value;
32266         }
32267         return parseFloat(value).toFixed(this.decimalPrecision);
32268     },
32269
32270     setValue : function(v)
32271     {
32272         v = this.fixPrecision(v);
32273         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32274     },
32275
32276     decimalPrecisionFcn : function(v)
32277     {
32278         return Math.floor(v);
32279     },
32280
32281     beforeBlur : function()
32282     {
32283         if(!this.castInt){
32284             return;
32285         }
32286         
32287         var v = this.parseValue(this.getRawValue());
32288         if(v){
32289             this.setValue(v);
32290         }
32291     }
32292     
32293 });
32294
32295  
32296
32297 /*
32298 * Licence: LGPL
32299 */
32300
32301 /**
32302  * @class Roo.bootstrap.DocumentSlider
32303  * @extends Roo.bootstrap.Component
32304  * Bootstrap DocumentSlider class
32305  * 
32306  * @constructor
32307  * Create a new DocumentViewer
32308  * @param {Object} config The config object
32309  */
32310
32311 Roo.bootstrap.DocumentSlider = function(config){
32312     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32313     
32314     this.files = [];
32315     
32316     this.addEvents({
32317         /**
32318          * @event initial
32319          * Fire after initEvent
32320          * @param {Roo.bootstrap.DocumentSlider} this
32321          */
32322         "initial" : true,
32323         /**
32324          * @event update
32325          * Fire after update
32326          * @param {Roo.bootstrap.DocumentSlider} this
32327          */
32328         "update" : true,
32329         /**
32330          * @event click
32331          * Fire after click
32332          * @param {Roo.bootstrap.DocumentSlider} this
32333          */
32334         "click" : true
32335     });
32336 };
32337
32338 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32339     
32340     files : false,
32341     
32342     indicator : 0,
32343     
32344     getAutoCreate : function()
32345     {
32346         var cfg = {
32347             tag : 'div',
32348             cls : 'roo-document-slider',
32349             cn : [
32350                 {
32351                     tag : 'div',
32352                     cls : 'roo-document-slider-header',
32353                     cn : [
32354                         {
32355                             tag : 'div',
32356                             cls : 'roo-document-slider-header-title'
32357                         }
32358                     ]
32359                 },
32360                 {
32361                     tag : 'div',
32362                     cls : 'roo-document-slider-body',
32363                     cn : [
32364                         {
32365                             tag : 'div',
32366                             cls : 'roo-document-slider-prev',
32367                             cn : [
32368                                 {
32369                                     tag : 'i',
32370                                     cls : 'fa fa-chevron-left'
32371                                 }
32372                             ]
32373                         },
32374                         {
32375                             tag : 'div',
32376                             cls : 'roo-document-slider-thumb',
32377                             cn : [
32378                                 {
32379                                     tag : 'img',
32380                                     cls : 'roo-document-slider-image'
32381                                 }
32382                             ]
32383                         },
32384                         {
32385                             tag : 'div',
32386                             cls : 'roo-document-slider-next',
32387                             cn : [
32388                                 {
32389                                     tag : 'i',
32390                                     cls : 'fa fa-chevron-right'
32391                                 }
32392                             ]
32393                         }
32394                     ]
32395                 }
32396             ]
32397         };
32398         
32399         return cfg;
32400     },
32401     
32402     initEvents : function()
32403     {
32404         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32405         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32406         
32407         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32408         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32409         
32410         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32411         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32412         
32413         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32414         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32415         
32416         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32417         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32418         
32419         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32420         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32421         
32422         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32423         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32424         
32425         this.thumbEl.on('click', this.onClick, this);
32426         
32427         this.prevIndicator.on('click', this.prev, this);
32428         
32429         this.nextIndicator.on('click', this.next, this);
32430         
32431     },
32432     
32433     initial : function()
32434     {
32435         if(this.files.length){
32436             this.indicator = 1;
32437             this.update()
32438         }
32439         
32440         this.fireEvent('initial', this);
32441     },
32442     
32443     update : function()
32444     {
32445         this.imageEl.attr('src', this.files[this.indicator - 1]);
32446         
32447         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32448         
32449         this.prevIndicator.show();
32450         
32451         if(this.indicator == 1){
32452             this.prevIndicator.hide();
32453         }
32454         
32455         this.nextIndicator.show();
32456         
32457         if(this.indicator == this.files.length){
32458             this.nextIndicator.hide();
32459         }
32460         
32461         this.thumbEl.scrollTo('top');
32462         
32463         this.fireEvent('update', this);
32464     },
32465     
32466     onClick : function(e)
32467     {
32468         e.preventDefault();
32469         
32470         this.fireEvent('click', this);
32471     },
32472     
32473     prev : function(e)
32474     {
32475         e.preventDefault();
32476         
32477         this.indicator = Math.max(1, this.indicator - 1);
32478         
32479         this.update();
32480     },
32481     
32482     next : function(e)
32483     {
32484         e.preventDefault();
32485         
32486         this.indicator = Math.min(this.files.length, this.indicator + 1);
32487         
32488         this.update();
32489     }
32490 });
32491 /*
32492  * - LGPL
32493  *
32494  * RadioSet
32495  *
32496  *
32497  */
32498
32499 /**
32500  * @class Roo.bootstrap.RadioSet
32501  * @extends Roo.bootstrap.Input
32502  * Bootstrap RadioSet class
32503  * @cfg {String} indicatorpos (left|right) default left
32504  * @cfg {Boolean} inline (true|false) inline the element (default true)
32505  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32506  * @constructor
32507  * Create a new RadioSet
32508  * @param {Object} config The config object
32509  */
32510
32511 Roo.bootstrap.RadioSet = function(config){
32512     
32513     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32514     
32515     this.radioes = [];
32516     
32517     Roo.bootstrap.RadioSet.register(this);
32518     
32519     this.addEvents({
32520         /**
32521         * @event check
32522         * Fires when the element is checked or unchecked.
32523         * @param {Roo.bootstrap.RadioSet} this This radio
32524         * @param {Roo.bootstrap.Radio} item The checked item
32525         */
32526        check : true
32527     });
32528     
32529 };
32530
32531 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32532
32533     radioes : false,
32534     
32535     inline : true,
32536     
32537     weight : '',
32538     
32539     indicatorpos : 'left',
32540     
32541     getAutoCreate : function()
32542     {
32543         var label = {
32544             tag : 'label',
32545             cls : 'roo-radio-set-label',
32546             cn : [
32547                 {
32548                     tag : 'span',
32549                     html : this.fieldLabel
32550                 }
32551             ]
32552         };
32553         
32554         if(this.indicatorpos == 'left'){
32555             label.cn.unshift({
32556                 tag : 'i',
32557                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32558                 tooltip : 'This field is required'
32559             });
32560         } else {
32561             label.cn.push({
32562                 tag : 'i',
32563                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32564                 tooltip : 'This field is required'
32565             });
32566         }
32567         
32568         var items = {
32569             tag : 'div',
32570             cls : 'roo-radio-set-items'
32571         };
32572         
32573         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32574         
32575         if (align === 'left' && this.fieldLabel.length) {
32576             
32577             items = {
32578                 cls : "roo-radio-set-right", 
32579                 cn: [
32580                     items
32581                 ]
32582             };
32583             
32584             if(this.labelWidth > 12){
32585                 label.style = "width: " + this.labelWidth + 'px';
32586             }
32587             
32588             if(this.labelWidth < 13 && this.labelmd == 0){
32589                 this.labelmd = this.labelWidth;
32590             }
32591             
32592             if(this.labellg > 0){
32593                 label.cls += ' col-lg-' + this.labellg;
32594                 items.cls += ' col-lg-' + (12 - this.labellg);
32595             }
32596             
32597             if(this.labelmd > 0){
32598                 label.cls += ' col-md-' + this.labelmd;
32599                 items.cls += ' col-md-' + (12 - this.labelmd);
32600             }
32601             
32602             if(this.labelsm > 0){
32603                 label.cls += ' col-sm-' + this.labelsm;
32604                 items.cls += ' col-sm-' + (12 - this.labelsm);
32605             }
32606             
32607             if(this.labelxs > 0){
32608                 label.cls += ' col-xs-' + this.labelxs;
32609                 items.cls += ' col-xs-' + (12 - this.labelxs);
32610             }
32611         }
32612         
32613         var cfg = {
32614             tag : 'div',
32615             cls : 'roo-radio-set',
32616             cn : [
32617                 {
32618                     tag : 'input',
32619                     cls : 'roo-radio-set-input',
32620                     type : 'hidden',
32621                     name : this.name,
32622                     value : this.value ? this.value :  ''
32623                 },
32624                 label,
32625                 items
32626             ]
32627         };
32628         
32629         if(this.weight.length){
32630             cfg.cls += ' roo-radio-' + this.weight;
32631         }
32632         
32633         if(this.inline) {
32634             cfg.cls += ' roo-radio-set-inline';
32635         }
32636         
32637         return cfg;
32638         
32639     },
32640
32641     initEvents : function()
32642     {
32643         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32644         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32645         
32646         if(!this.fieldLabel.length){
32647             this.labelEl.hide();
32648         }
32649         
32650         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32651         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32652         
32653         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32654         this.indicatorEl().hide();
32655         
32656         this.originalValue = this.getValue();
32657         
32658     },
32659     
32660     inputEl: function ()
32661     {
32662         return this.el.select('.roo-radio-set-input', true).first();
32663     },
32664     
32665     getChildContainer : function()
32666     {
32667         return this.itemsEl;
32668     },
32669     
32670     register : function(item)
32671     {
32672         this.radioes.push(item);
32673         
32674     },
32675     
32676     validate : function()
32677     {   
32678         var valid = false;
32679         
32680         Roo.each(this.radioes, function(i){
32681             if(!i.checked){
32682                 return;
32683             }
32684             
32685             valid = true;
32686             return false;
32687         });
32688         
32689         if(this.allowBlank) {
32690             return true;
32691         }
32692         
32693         if(this.disabled || valid){
32694             this.markValid();
32695             return true;
32696         }
32697         
32698         this.markInvalid();
32699         return false;
32700         
32701     },
32702     
32703     markValid : function()
32704     {
32705         if(this.labelEl.isVisible(true)){
32706             this.indicatorEl().hide();
32707         }
32708         
32709         this.el.removeClass([this.invalidClass, this.validClass]);
32710         this.el.addClass(this.validClass);
32711         
32712         this.fireEvent('valid', this);
32713     },
32714     
32715     markInvalid : function(msg)
32716     {
32717         if(this.allowBlank || this.disabled){
32718             return;
32719         }
32720         
32721         if(this.labelEl.isVisible(true)){
32722             this.indicatorEl().show();
32723         }
32724         
32725         this.el.removeClass([this.invalidClass, this.validClass]);
32726         this.el.addClass(this.invalidClass);
32727         
32728         this.fireEvent('invalid', this, msg);
32729         
32730     },
32731     
32732     setValue : function(v, suppressEvent)
32733     {   
32734         Roo.each(this.radioes, function(i){
32735             
32736             i.checked = false;
32737             i.el.removeClass('checked');
32738             
32739             if(i.value === v || i.value.toString() === v.toString()){
32740                 i.checked = true;
32741                 i.el.addClass('checked');
32742                 
32743                 if(suppressEvent !== true){
32744                     this.fireEvent('check', this, i);
32745                 }
32746             }
32747             
32748         }, this);
32749         
32750         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32751         
32752     },
32753     
32754     clearInvalid : function(){
32755         
32756         if(!this.el || this.preventMark){
32757             return;
32758         }
32759         
32760         if(this.labelEl.isVisible(true)){
32761             this.indicatorEl().hide();
32762         }
32763         
32764         this.el.removeClass([this.invalidClass]);
32765         
32766         this.fireEvent('valid', this);
32767     }
32768     
32769 });
32770
32771 Roo.apply(Roo.bootstrap.RadioSet, {
32772     
32773     groups: {},
32774     
32775     register : function(set)
32776     {
32777         this.groups[set.name] = set;
32778     },
32779     
32780     get: function(name) 
32781     {
32782         if (typeof(this.groups[name]) == 'undefined') {
32783             return false;
32784         }
32785         
32786         return this.groups[name] ;
32787     }
32788     
32789 });
32790 /*
32791  * Based on:
32792  * Ext JS Library 1.1.1
32793  * Copyright(c) 2006-2007, Ext JS, LLC.
32794  *
32795  * Originally Released Under LGPL - original licence link has changed is not relivant.
32796  *
32797  * Fork - LGPL
32798  * <script type="text/javascript">
32799  */
32800
32801
32802 /**
32803  * @class Roo.bootstrap.SplitBar
32804  * @extends Roo.util.Observable
32805  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32806  * <br><br>
32807  * Usage:
32808  * <pre><code>
32809 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32810                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32811 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32812 split.minSize = 100;
32813 split.maxSize = 600;
32814 split.animate = true;
32815 split.on('moved', splitterMoved);
32816 </code></pre>
32817  * @constructor
32818  * Create a new SplitBar
32819  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32820  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32821  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32822  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32823                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32824                         position of the SplitBar).
32825  */
32826 Roo.bootstrap.SplitBar = function(cfg){
32827     
32828     /** @private */
32829     
32830     //{
32831     //  dragElement : elm
32832     //  resizingElement: el,
32833         // optional..
32834     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32835     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32836         // existingProxy ???
32837     //}
32838     
32839     this.el = Roo.get(cfg.dragElement, true);
32840     this.el.dom.unselectable = "on";
32841     /** @private */
32842     this.resizingEl = Roo.get(cfg.resizingElement, true);
32843
32844     /**
32845      * @private
32846      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32847      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32848      * @type Number
32849      */
32850     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32851     
32852     /**
32853      * The minimum size of the resizing element. (Defaults to 0)
32854      * @type Number
32855      */
32856     this.minSize = 0;
32857     
32858     /**
32859      * The maximum size of the resizing element. (Defaults to 2000)
32860      * @type Number
32861      */
32862     this.maxSize = 2000;
32863     
32864     /**
32865      * Whether to animate the transition to the new size
32866      * @type Boolean
32867      */
32868     this.animate = false;
32869     
32870     /**
32871      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32872      * @type Boolean
32873      */
32874     this.useShim = false;
32875     
32876     /** @private */
32877     this.shim = null;
32878     
32879     if(!cfg.existingProxy){
32880         /** @private */
32881         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32882     }else{
32883         this.proxy = Roo.get(cfg.existingProxy).dom;
32884     }
32885     /** @private */
32886     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32887     
32888     /** @private */
32889     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32890     
32891     /** @private */
32892     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32893     
32894     /** @private */
32895     this.dragSpecs = {};
32896     
32897     /**
32898      * @private The adapter to use to positon and resize elements
32899      */
32900     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32901     this.adapter.init(this);
32902     
32903     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32904         /** @private */
32905         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32906         this.el.addClass("roo-splitbar-h");
32907     }else{
32908         /** @private */
32909         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32910         this.el.addClass("roo-splitbar-v");
32911     }
32912     
32913     this.addEvents({
32914         /**
32915          * @event resize
32916          * Fires when the splitter is moved (alias for {@link #event-moved})
32917          * @param {Roo.bootstrap.SplitBar} this
32918          * @param {Number} newSize the new width or height
32919          */
32920         "resize" : true,
32921         /**
32922          * @event moved
32923          * Fires when the splitter is moved
32924          * @param {Roo.bootstrap.SplitBar} this
32925          * @param {Number} newSize the new width or height
32926          */
32927         "moved" : true,
32928         /**
32929          * @event beforeresize
32930          * Fires before the splitter is dragged
32931          * @param {Roo.bootstrap.SplitBar} this
32932          */
32933         "beforeresize" : true,
32934
32935         "beforeapply" : true
32936     });
32937
32938     Roo.util.Observable.call(this);
32939 };
32940
32941 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32942     onStartProxyDrag : function(x, y){
32943         this.fireEvent("beforeresize", this);
32944         if(!this.overlay){
32945             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32946             o.unselectable();
32947             o.enableDisplayMode("block");
32948             // all splitbars share the same overlay
32949             Roo.bootstrap.SplitBar.prototype.overlay = o;
32950         }
32951         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32952         this.overlay.show();
32953         Roo.get(this.proxy).setDisplayed("block");
32954         var size = this.adapter.getElementSize(this);
32955         this.activeMinSize = this.getMinimumSize();;
32956         this.activeMaxSize = this.getMaximumSize();;
32957         var c1 = size - this.activeMinSize;
32958         var c2 = Math.max(this.activeMaxSize - size, 0);
32959         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32960             this.dd.resetConstraints();
32961             this.dd.setXConstraint(
32962                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32963                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32964             );
32965             this.dd.setYConstraint(0, 0);
32966         }else{
32967             this.dd.resetConstraints();
32968             this.dd.setXConstraint(0, 0);
32969             this.dd.setYConstraint(
32970                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32971                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32972             );
32973          }
32974         this.dragSpecs.startSize = size;
32975         this.dragSpecs.startPoint = [x, y];
32976         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32977     },
32978     
32979     /** 
32980      * @private Called after the drag operation by the DDProxy
32981      */
32982     onEndProxyDrag : function(e){
32983         Roo.get(this.proxy).setDisplayed(false);
32984         var endPoint = Roo.lib.Event.getXY(e);
32985         if(this.overlay){
32986             this.overlay.hide();
32987         }
32988         var newSize;
32989         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32990             newSize = this.dragSpecs.startSize + 
32991                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32992                     endPoint[0] - this.dragSpecs.startPoint[0] :
32993                     this.dragSpecs.startPoint[0] - endPoint[0]
32994                 );
32995         }else{
32996             newSize = this.dragSpecs.startSize + 
32997                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32998                     endPoint[1] - this.dragSpecs.startPoint[1] :
32999                     this.dragSpecs.startPoint[1] - endPoint[1]
33000                 );
33001         }
33002         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33003         if(newSize != this.dragSpecs.startSize){
33004             if(this.fireEvent('beforeapply', this, newSize) !== false){
33005                 this.adapter.setElementSize(this, newSize);
33006                 this.fireEvent("moved", this, newSize);
33007                 this.fireEvent("resize", this, newSize);
33008             }
33009         }
33010     },
33011     
33012     /**
33013      * Get the adapter this SplitBar uses
33014      * @return The adapter object
33015      */
33016     getAdapter : function(){
33017         return this.adapter;
33018     },
33019     
33020     /**
33021      * Set the adapter this SplitBar uses
33022      * @param {Object} adapter A SplitBar adapter object
33023      */
33024     setAdapter : function(adapter){
33025         this.adapter = adapter;
33026         this.adapter.init(this);
33027     },
33028     
33029     /**
33030      * Gets the minimum size for the resizing element
33031      * @return {Number} The minimum size
33032      */
33033     getMinimumSize : function(){
33034         return this.minSize;
33035     },
33036     
33037     /**
33038      * Sets the minimum size for the resizing element
33039      * @param {Number} minSize The minimum size
33040      */
33041     setMinimumSize : function(minSize){
33042         this.minSize = minSize;
33043     },
33044     
33045     /**
33046      * Gets the maximum size for the resizing element
33047      * @return {Number} The maximum size
33048      */
33049     getMaximumSize : function(){
33050         return this.maxSize;
33051     },
33052     
33053     /**
33054      * Sets the maximum size for the resizing element
33055      * @param {Number} maxSize The maximum size
33056      */
33057     setMaximumSize : function(maxSize){
33058         this.maxSize = maxSize;
33059     },
33060     
33061     /**
33062      * Sets the initialize size for the resizing element
33063      * @param {Number} size The initial size
33064      */
33065     setCurrentSize : function(size){
33066         var oldAnimate = this.animate;
33067         this.animate = false;
33068         this.adapter.setElementSize(this, size);
33069         this.animate = oldAnimate;
33070     },
33071     
33072     /**
33073      * Destroy this splitbar. 
33074      * @param {Boolean} removeEl True to remove the element
33075      */
33076     destroy : function(removeEl){
33077         if(this.shim){
33078             this.shim.remove();
33079         }
33080         this.dd.unreg();
33081         this.proxy.parentNode.removeChild(this.proxy);
33082         if(removeEl){
33083             this.el.remove();
33084         }
33085     }
33086 });
33087
33088 /**
33089  * @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.
33090  */
33091 Roo.bootstrap.SplitBar.createProxy = function(dir){
33092     var proxy = new Roo.Element(document.createElement("div"));
33093     proxy.unselectable();
33094     var cls = 'roo-splitbar-proxy';
33095     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33096     document.body.appendChild(proxy.dom);
33097     return proxy.dom;
33098 };
33099
33100 /** 
33101  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33102  * Default Adapter. It assumes the splitter and resizing element are not positioned
33103  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33104  */
33105 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33106 };
33107
33108 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33109     // do nothing for now
33110     init : function(s){
33111     
33112     },
33113     /**
33114      * Called before drag operations to get the current size of the resizing element. 
33115      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33116      */
33117      getElementSize : function(s){
33118         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33119             return s.resizingEl.getWidth();
33120         }else{
33121             return s.resizingEl.getHeight();
33122         }
33123     },
33124     
33125     /**
33126      * Called after drag operations to set the size of the resizing element.
33127      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33128      * @param {Number} newSize The new size to set
33129      * @param {Function} onComplete A function to be invoked when resizing is complete
33130      */
33131     setElementSize : function(s, newSize, onComplete){
33132         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33133             if(!s.animate){
33134                 s.resizingEl.setWidth(newSize);
33135                 if(onComplete){
33136                     onComplete(s, newSize);
33137                 }
33138             }else{
33139                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33140             }
33141         }else{
33142             
33143             if(!s.animate){
33144                 s.resizingEl.setHeight(newSize);
33145                 if(onComplete){
33146                     onComplete(s, newSize);
33147                 }
33148             }else{
33149                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33150             }
33151         }
33152     }
33153 };
33154
33155 /** 
33156  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33157  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33158  * Adapter that  moves the splitter element to align with the resized sizing element. 
33159  * Used with an absolute positioned SplitBar.
33160  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33161  * document.body, make sure you assign an id to the body element.
33162  */
33163 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33164     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33165     this.container = Roo.get(container);
33166 };
33167
33168 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33169     init : function(s){
33170         this.basic.init(s);
33171     },
33172     
33173     getElementSize : function(s){
33174         return this.basic.getElementSize(s);
33175     },
33176     
33177     setElementSize : function(s, newSize, onComplete){
33178         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33179     },
33180     
33181     moveSplitter : function(s){
33182         var yes = Roo.bootstrap.SplitBar;
33183         switch(s.placement){
33184             case yes.LEFT:
33185                 s.el.setX(s.resizingEl.getRight());
33186                 break;
33187             case yes.RIGHT:
33188                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33189                 break;
33190             case yes.TOP:
33191                 s.el.setY(s.resizingEl.getBottom());
33192                 break;
33193             case yes.BOTTOM:
33194                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33195                 break;
33196         }
33197     }
33198 };
33199
33200 /**
33201  * Orientation constant - Create a vertical SplitBar
33202  * @static
33203  * @type Number
33204  */
33205 Roo.bootstrap.SplitBar.VERTICAL = 1;
33206
33207 /**
33208  * Orientation constant - Create a horizontal SplitBar
33209  * @static
33210  * @type Number
33211  */
33212 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33213
33214 /**
33215  * Placement constant - The resizing element is to the left of the splitter element
33216  * @static
33217  * @type Number
33218  */
33219 Roo.bootstrap.SplitBar.LEFT = 1;
33220
33221 /**
33222  * Placement constant - The resizing element is to the right of the splitter element
33223  * @static
33224  * @type Number
33225  */
33226 Roo.bootstrap.SplitBar.RIGHT = 2;
33227
33228 /**
33229  * Placement constant - The resizing element is positioned above the splitter element
33230  * @static
33231  * @type Number
33232  */
33233 Roo.bootstrap.SplitBar.TOP = 3;
33234
33235 /**
33236  * Placement constant - The resizing element is positioned under splitter element
33237  * @static
33238  * @type Number
33239  */
33240 Roo.bootstrap.SplitBar.BOTTOM = 4;
33241 Roo.namespace("Roo.bootstrap.layout");/*
33242  * Based on:
33243  * Ext JS Library 1.1.1
33244  * Copyright(c) 2006-2007, Ext JS, LLC.
33245  *
33246  * Originally Released Under LGPL - original licence link has changed is not relivant.
33247  *
33248  * Fork - LGPL
33249  * <script type="text/javascript">
33250  */
33251
33252 /**
33253  * @class Roo.bootstrap.layout.Manager
33254  * @extends Roo.bootstrap.Component
33255  * Base class for layout managers.
33256  */
33257 Roo.bootstrap.layout.Manager = function(config)
33258 {
33259     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33260
33261
33262
33263
33264
33265     /** false to disable window resize monitoring @type Boolean */
33266     this.monitorWindowResize = true;
33267     this.regions = {};
33268     this.addEvents({
33269         /**
33270          * @event layout
33271          * Fires when a layout is performed.
33272          * @param {Roo.LayoutManager} this
33273          */
33274         "layout" : true,
33275         /**
33276          * @event regionresized
33277          * Fires when the user resizes a region.
33278          * @param {Roo.LayoutRegion} region The resized region
33279          * @param {Number} newSize The new size (width for east/west, height for north/south)
33280          */
33281         "regionresized" : true,
33282         /**
33283          * @event regioncollapsed
33284          * Fires when a region is collapsed.
33285          * @param {Roo.LayoutRegion} region The collapsed region
33286          */
33287         "regioncollapsed" : true,
33288         /**
33289          * @event regionexpanded
33290          * Fires when a region is expanded.
33291          * @param {Roo.LayoutRegion} region The expanded region
33292          */
33293         "regionexpanded" : true
33294     });
33295     this.updating = false;
33296
33297     if (config.el) {
33298         this.el = Roo.get(config.el);
33299         this.initEvents();
33300     }
33301
33302 };
33303
33304 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33305
33306
33307     regions : null,
33308
33309     monitorWindowResize : true,
33310
33311
33312     updating : false,
33313
33314
33315     onRender : function(ct, position)
33316     {
33317         if(!this.el){
33318             this.el = Roo.get(ct);
33319             this.initEvents();
33320         }
33321         //this.fireEvent('render',this);
33322     },
33323
33324
33325     initEvents: function()
33326     {
33327
33328
33329         // ie scrollbar fix
33330         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33331             document.body.scroll = "no";
33332         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33333             this.el.position('relative');
33334         }
33335         this.id = this.el.id;
33336         this.el.addClass("roo-layout-container");
33337         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33338         if(this.el.dom != document.body ) {
33339             this.el.on('resize', this.layout,this);
33340             this.el.on('show', this.layout,this);
33341         }
33342
33343     },
33344
33345     /**
33346      * Returns true if this layout is currently being updated
33347      * @return {Boolean}
33348      */
33349     isUpdating : function(){
33350         return this.updating;
33351     },
33352
33353     /**
33354      * Suspend the LayoutManager from doing auto-layouts while
33355      * making multiple add or remove calls
33356      */
33357     beginUpdate : function(){
33358         this.updating = true;
33359     },
33360
33361     /**
33362      * Restore auto-layouts and optionally disable the manager from performing a layout
33363      * @param {Boolean} noLayout true to disable a layout update
33364      */
33365     endUpdate : function(noLayout){
33366         this.updating = false;
33367         if(!noLayout){
33368             this.layout();
33369         }
33370     },
33371
33372     layout: function(){
33373         // abstract...
33374     },
33375
33376     onRegionResized : function(region, newSize){
33377         this.fireEvent("regionresized", region, newSize);
33378         this.layout();
33379     },
33380
33381     onRegionCollapsed : function(region){
33382         this.fireEvent("regioncollapsed", region);
33383     },
33384
33385     onRegionExpanded : function(region){
33386         this.fireEvent("regionexpanded", region);
33387     },
33388
33389     /**
33390      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33391      * performs box-model adjustments.
33392      * @return {Object} The size as an object {width: (the width), height: (the height)}
33393      */
33394     getViewSize : function()
33395     {
33396         var size;
33397         if(this.el.dom != document.body){
33398             size = this.el.getSize();
33399         }else{
33400             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33401         }
33402         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33403         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33404         return size;
33405     },
33406
33407     /**
33408      * Returns the Element this layout is bound to.
33409      * @return {Roo.Element}
33410      */
33411     getEl : function(){
33412         return this.el;
33413     },
33414
33415     /**
33416      * Returns the specified region.
33417      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33418      * @return {Roo.LayoutRegion}
33419      */
33420     getRegion : function(target){
33421         return this.regions[target.toLowerCase()];
33422     },
33423
33424     onWindowResize : function(){
33425         if(this.monitorWindowResize){
33426             this.layout();
33427         }
33428     }
33429 });
33430 /*
33431  * Based on:
33432  * Ext JS Library 1.1.1
33433  * Copyright(c) 2006-2007, Ext JS, LLC.
33434  *
33435  * Originally Released Under LGPL - original licence link has changed is not relivant.
33436  *
33437  * Fork - LGPL
33438  * <script type="text/javascript">
33439  */
33440 /**
33441  * @class Roo.bootstrap.layout.Border
33442  * @extends Roo.bootstrap.layout.Manager
33443  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33444  * please see: examples/bootstrap/nested.html<br><br>
33445  
33446 <b>The container the layout is rendered into can be either the body element or any other element.
33447 If it is not the body element, the container needs to either be an absolute positioned element,
33448 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33449 the container size if it is not the body element.</b>
33450
33451 * @constructor
33452 * Create a new Border
33453 * @param {Object} config Configuration options
33454  */
33455 Roo.bootstrap.layout.Border = function(config){
33456     config = config || {};
33457     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33458     
33459     
33460     
33461     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33462         if(config[region]){
33463             config[region].region = region;
33464             this.addRegion(config[region]);
33465         }
33466     },this);
33467     
33468 };
33469
33470 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33471
33472 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33473     /**
33474      * Creates and adds a new region if it doesn't already exist.
33475      * @param {String} target The target region key (north, south, east, west or center).
33476      * @param {Object} config The regions config object
33477      * @return {BorderLayoutRegion} The new region
33478      */
33479     addRegion : function(config)
33480     {
33481         if(!this.regions[config.region]){
33482             var r = this.factory(config);
33483             this.bindRegion(r);
33484         }
33485         return this.regions[config.region];
33486     },
33487
33488     // private (kinda)
33489     bindRegion : function(r){
33490         this.regions[r.config.region] = r;
33491         
33492         r.on("visibilitychange",    this.layout, this);
33493         r.on("paneladded",          this.layout, this);
33494         r.on("panelremoved",        this.layout, this);
33495         r.on("invalidated",         this.layout, this);
33496         r.on("resized",             this.onRegionResized, this);
33497         r.on("collapsed",           this.onRegionCollapsed, this);
33498         r.on("expanded",            this.onRegionExpanded, this);
33499     },
33500
33501     /**
33502      * Performs a layout update.
33503      */
33504     layout : function()
33505     {
33506         if(this.updating) {
33507             return;
33508         }
33509         
33510         // render all the rebions if they have not been done alreayd?
33511         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33512             if(this.regions[region] && !this.regions[region].bodyEl){
33513                 this.regions[region].onRender(this.el)
33514             }
33515         },this);
33516         
33517         var size = this.getViewSize();
33518         var w = size.width;
33519         var h = size.height;
33520         var centerW = w;
33521         var centerH = h;
33522         var centerY = 0;
33523         var centerX = 0;
33524         //var x = 0, y = 0;
33525
33526         var rs = this.regions;
33527         var north = rs["north"];
33528         var south = rs["south"]; 
33529         var west = rs["west"];
33530         var east = rs["east"];
33531         var center = rs["center"];
33532         //if(this.hideOnLayout){ // not supported anymore
33533             //c.el.setStyle("display", "none");
33534         //}
33535         if(north && north.isVisible()){
33536             var b = north.getBox();
33537             var m = north.getMargins();
33538             b.width = w - (m.left+m.right);
33539             b.x = m.left;
33540             b.y = m.top;
33541             centerY = b.height + b.y + m.bottom;
33542             centerH -= centerY;
33543             north.updateBox(this.safeBox(b));
33544         }
33545         if(south && south.isVisible()){
33546             var b = south.getBox();
33547             var m = south.getMargins();
33548             b.width = w - (m.left+m.right);
33549             b.x = m.left;
33550             var totalHeight = (b.height + m.top + m.bottom);
33551             b.y = h - totalHeight + m.top;
33552             centerH -= totalHeight;
33553             south.updateBox(this.safeBox(b));
33554         }
33555         if(west && west.isVisible()){
33556             var b = west.getBox();
33557             var m = west.getMargins();
33558             b.height = centerH - (m.top+m.bottom);
33559             b.x = m.left;
33560             b.y = centerY + m.top;
33561             var totalWidth = (b.width + m.left + m.right);
33562             centerX += totalWidth;
33563             centerW -= totalWidth;
33564             west.updateBox(this.safeBox(b));
33565         }
33566         if(east && east.isVisible()){
33567             var b = east.getBox();
33568             var m = east.getMargins();
33569             b.height = centerH - (m.top+m.bottom);
33570             var totalWidth = (b.width + m.left + m.right);
33571             b.x = w - totalWidth + m.left;
33572             b.y = centerY + m.top;
33573             centerW -= totalWidth;
33574             east.updateBox(this.safeBox(b));
33575         }
33576         if(center){
33577             var m = center.getMargins();
33578             var centerBox = {
33579                 x: centerX + m.left,
33580                 y: centerY + m.top,
33581                 width: centerW - (m.left+m.right),
33582                 height: centerH - (m.top+m.bottom)
33583             };
33584             //if(this.hideOnLayout){
33585                 //center.el.setStyle("display", "block");
33586             //}
33587             center.updateBox(this.safeBox(centerBox));
33588         }
33589         this.el.repaint();
33590         this.fireEvent("layout", this);
33591     },
33592
33593     // private
33594     safeBox : function(box){
33595         box.width = Math.max(0, box.width);
33596         box.height = Math.max(0, box.height);
33597         return box;
33598     },
33599
33600     /**
33601      * Adds a ContentPanel (or subclass) to this layout.
33602      * @param {String} target The target region key (north, south, east, west or center).
33603      * @param {Roo.ContentPanel} panel The panel to add
33604      * @return {Roo.ContentPanel} The added panel
33605      */
33606     add : function(target, panel){
33607          
33608         target = target.toLowerCase();
33609         return this.regions[target].add(panel);
33610     },
33611
33612     /**
33613      * Remove a ContentPanel (or subclass) to this layout.
33614      * @param {String} target The target region key (north, south, east, west or center).
33615      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33616      * @return {Roo.ContentPanel} The removed panel
33617      */
33618     remove : function(target, panel){
33619         target = target.toLowerCase();
33620         return this.regions[target].remove(panel);
33621     },
33622
33623     /**
33624      * Searches all regions for a panel with the specified id
33625      * @param {String} panelId
33626      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33627      */
33628     findPanel : function(panelId){
33629         var rs = this.regions;
33630         for(var target in rs){
33631             if(typeof rs[target] != "function"){
33632                 var p = rs[target].getPanel(panelId);
33633                 if(p){
33634                     return p;
33635                 }
33636             }
33637         }
33638         return null;
33639     },
33640
33641     /**
33642      * Searches all regions for a panel with the specified id and activates (shows) it.
33643      * @param {String/ContentPanel} panelId The panels id or the panel itself
33644      * @return {Roo.ContentPanel} The shown panel or null
33645      */
33646     showPanel : function(panelId) {
33647       var rs = this.regions;
33648       for(var target in rs){
33649          var r = rs[target];
33650          if(typeof r != "function"){
33651             if(r.hasPanel(panelId)){
33652                return r.showPanel(panelId);
33653             }
33654          }
33655       }
33656       return null;
33657    },
33658
33659    /**
33660      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33661      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33662      */
33663    /*
33664     restoreState : function(provider){
33665         if(!provider){
33666             provider = Roo.state.Manager;
33667         }
33668         var sm = new Roo.LayoutStateManager();
33669         sm.init(this, provider);
33670     },
33671 */
33672  
33673  
33674     /**
33675      * Adds a xtype elements to the layout.
33676      * <pre><code>
33677
33678 layout.addxtype({
33679        xtype : 'ContentPanel',
33680        region: 'west',
33681        items: [ .... ]
33682    }
33683 );
33684
33685 layout.addxtype({
33686         xtype : 'NestedLayoutPanel',
33687         region: 'west',
33688         layout: {
33689            center: { },
33690            west: { }   
33691         },
33692         items : [ ... list of content panels or nested layout panels.. ]
33693    }
33694 );
33695 </code></pre>
33696      * @param {Object} cfg Xtype definition of item to add.
33697      */
33698     addxtype : function(cfg)
33699     {
33700         // basically accepts a pannel...
33701         // can accept a layout region..!?!?
33702         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33703         
33704         
33705         // theory?  children can only be panels??
33706         
33707         //if (!cfg.xtype.match(/Panel$/)) {
33708         //    return false;
33709         //}
33710         var ret = false;
33711         
33712         if (typeof(cfg.region) == 'undefined') {
33713             Roo.log("Failed to add Panel, region was not set");
33714             Roo.log(cfg);
33715             return false;
33716         }
33717         var region = cfg.region;
33718         delete cfg.region;
33719         
33720           
33721         var xitems = [];
33722         if (cfg.items) {
33723             xitems = cfg.items;
33724             delete cfg.items;
33725         }
33726         var nb = false;
33727         
33728         switch(cfg.xtype) 
33729         {
33730             case 'Content':  // ContentPanel (el, cfg)
33731             case 'Scroll':  // ContentPanel (el, cfg)
33732             case 'View': 
33733                 cfg.autoCreate = true;
33734                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33735                 //} else {
33736                 //    var el = this.el.createChild();
33737                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33738                 //}
33739                 
33740                 this.add(region, ret);
33741                 break;
33742             
33743             /*
33744             case 'TreePanel': // our new panel!
33745                 cfg.el = this.el.createChild();
33746                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33747                 this.add(region, ret);
33748                 break;
33749             */
33750             
33751             case 'Nest': 
33752                 // create a new Layout (which is  a Border Layout...
33753                 
33754                 var clayout = cfg.layout;
33755                 clayout.el  = this.el.createChild();
33756                 clayout.items   = clayout.items  || [];
33757                 
33758                 delete cfg.layout;
33759                 
33760                 // replace this exitems with the clayout ones..
33761                 xitems = clayout.items;
33762                  
33763                 // force background off if it's in center...
33764                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33765                     cfg.background = false;
33766                 }
33767                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33768                 
33769                 
33770                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33771                 //console.log('adding nested layout panel '  + cfg.toSource());
33772                 this.add(region, ret);
33773                 nb = {}; /// find first...
33774                 break;
33775             
33776             case 'Grid':
33777                 
33778                 // needs grid and region
33779                 
33780                 //var el = this.getRegion(region).el.createChild();
33781                 /*
33782                  *var el = this.el.createChild();
33783                 // create the grid first...
33784                 cfg.grid.container = el;
33785                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33786                 */
33787                 
33788                 if (region == 'center' && this.active ) {
33789                     cfg.background = false;
33790                 }
33791                 
33792                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33793                 
33794                 this.add(region, ret);
33795                 /*
33796                 if (cfg.background) {
33797                     // render grid on panel activation (if panel background)
33798                     ret.on('activate', function(gp) {
33799                         if (!gp.grid.rendered) {
33800                     //        gp.grid.render(el);
33801                         }
33802                     });
33803                 } else {
33804                   //  cfg.grid.render(el);
33805                 }
33806                 */
33807                 break;
33808            
33809            
33810             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33811                 // it was the old xcomponent building that caused this before.
33812                 // espeically if border is the top element in the tree.
33813                 ret = this;
33814                 break; 
33815                 
33816                     
33817                 
33818                 
33819                 
33820             default:
33821                 /*
33822                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33823                     
33824                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33825                     this.add(region, ret);
33826                 } else {
33827                 */
33828                     Roo.log(cfg);
33829                     throw "Can not add '" + cfg.xtype + "' to Border";
33830                     return null;
33831              
33832                                 
33833              
33834         }
33835         this.beginUpdate();
33836         // add children..
33837         var region = '';
33838         var abn = {};
33839         Roo.each(xitems, function(i)  {
33840             region = nb && i.region ? i.region : false;
33841             
33842             var add = ret.addxtype(i);
33843            
33844             if (region) {
33845                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33846                 if (!i.background) {
33847                     abn[region] = nb[region] ;
33848                 }
33849             }
33850             
33851         });
33852         this.endUpdate();
33853
33854         // make the last non-background panel active..
33855         //if (nb) { Roo.log(abn); }
33856         if (nb) {
33857             
33858             for(var r in abn) {
33859                 region = this.getRegion(r);
33860                 if (region) {
33861                     // tried using nb[r], but it does not work..
33862                      
33863                     region.showPanel(abn[r]);
33864                    
33865                 }
33866             }
33867         }
33868         return ret;
33869         
33870     },
33871     
33872     
33873 // private
33874     factory : function(cfg)
33875     {
33876         
33877         var validRegions = Roo.bootstrap.layout.Border.regions;
33878
33879         var target = cfg.region;
33880         cfg.mgr = this;
33881         
33882         var r = Roo.bootstrap.layout;
33883         Roo.log(target);
33884         switch(target){
33885             case "north":
33886                 return new r.North(cfg);
33887             case "south":
33888                 return new r.South(cfg);
33889             case "east":
33890                 return new r.East(cfg);
33891             case "west":
33892                 return new r.West(cfg);
33893             case "center":
33894                 return new r.Center(cfg);
33895         }
33896         throw 'Layout region "'+target+'" not supported.';
33897     }
33898     
33899     
33900 });
33901  /*
33902  * Based on:
33903  * Ext JS Library 1.1.1
33904  * Copyright(c) 2006-2007, Ext JS, LLC.
33905  *
33906  * Originally Released Under LGPL - original licence link has changed is not relivant.
33907  *
33908  * Fork - LGPL
33909  * <script type="text/javascript">
33910  */
33911  
33912 /**
33913  * @class Roo.bootstrap.layout.Basic
33914  * @extends Roo.util.Observable
33915  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33916  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33917  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33918  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33919  * @cfg {string}   region  the region that it inhabits..
33920  * @cfg {bool}   skipConfig skip config?
33921  * 
33922
33923  */
33924 Roo.bootstrap.layout.Basic = function(config){
33925     
33926     this.mgr = config.mgr;
33927     
33928     this.position = config.region;
33929     
33930     var skipConfig = config.skipConfig;
33931     
33932     this.events = {
33933         /**
33934          * @scope Roo.BasicLayoutRegion
33935          */
33936         
33937         /**
33938          * @event beforeremove
33939          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33940          * @param {Roo.LayoutRegion} this
33941          * @param {Roo.ContentPanel} panel The panel
33942          * @param {Object} e The cancel event object
33943          */
33944         "beforeremove" : true,
33945         /**
33946          * @event invalidated
33947          * Fires when the layout for this region is changed.
33948          * @param {Roo.LayoutRegion} this
33949          */
33950         "invalidated" : true,
33951         /**
33952          * @event visibilitychange
33953          * Fires when this region is shown or hidden 
33954          * @param {Roo.LayoutRegion} this
33955          * @param {Boolean} visibility true or false
33956          */
33957         "visibilitychange" : true,
33958         /**
33959          * @event paneladded
33960          * Fires when a panel is added. 
33961          * @param {Roo.LayoutRegion} this
33962          * @param {Roo.ContentPanel} panel The panel
33963          */
33964         "paneladded" : true,
33965         /**
33966          * @event panelremoved
33967          * Fires when a panel is removed. 
33968          * @param {Roo.LayoutRegion} this
33969          * @param {Roo.ContentPanel} panel The panel
33970          */
33971         "panelremoved" : true,
33972         /**
33973          * @event beforecollapse
33974          * Fires when this region before collapse.
33975          * @param {Roo.LayoutRegion} this
33976          */
33977         "beforecollapse" : true,
33978         /**
33979          * @event collapsed
33980          * Fires when this region is collapsed.
33981          * @param {Roo.LayoutRegion} this
33982          */
33983         "collapsed" : true,
33984         /**
33985          * @event expanded
33986          * Fires when this region is expanded.
33987          * @param {Roo.LayoutRegion} this
33988          */
33989         "expanded" : true,
33990         /**
33991          * @event slideshow
33992          * Fires when this region is slid into view.
33993          * @param {Roo.LayoutRegion} this
33994          */
33995         "slideshow" : true,
33996         /**
33997          * @event slidehide
33998          * Fires when this region slides out of view. 
33999          * @param {Roo.LayoutRegion} this
34000          */
34001         "slidehide" : true,
34002         /**
34003          * @event panelactivated
34004          * Fires when a panel is activated. 
34005          * @param {Roo.LayoutRegion} this
34006          * @param {Roo.ContentPanel} panel The activated panel
34007          */
34008         "panelactivated" : true,
34009         /**
34010          * @event resized
34011          * Fires when the user resizes this region. 
34012          * @param {Roo.LayoutRegion} this
34013          * @param {Number} newSize The new size (width for east/west, height for north/south)
34014          */
34015         "resized" : true
34016     };
34017     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34018     this.panels = new Roo.util.MixedCollection();
34019     this.panels.getKey = this.getPanelId.createDelegate(this);
34020     this.box = null;
34021     this.activePanel = null;
34022     // ensure listeners are added...
34023     
34024     if (config.listeners || config.events) {
34025         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34026             listeners : config.listeners || {},
34027             events : config.events || {}
34028         });
34029     }
34030     
34031     if(skipConfig !== true){
34032         this.applyConfig(config);
34033     }
34034 };
34035
34036 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34037 {
34038     getPanelId : function(p){
34039         return p.getId();
34040     },
34041     
34042     applyConfig : function(config){
34043         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34044         this.config = config;
34045         
34046     },
34047     
34048     /**
34049      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34050      * the width, for horizontal (north, south) the height.
34051      * @param {Number} newSize The new width or height
34052      */
34053     resizeTo : function(newSize){
34054         var el = this.el ? this.el :
34055                  (this.activePanel ? this.activePanel.getEl() : null);
34056         if(el){
34057             switch(this.position){
34058                 case "east":
34059                 case "west":
34060                     el.setWidth(newSize);
34061                     this.fireEvent("resized", this, newSize);
34062                 break;
34063                 case "north":
34064                 case "south":
34065                     el.setHeight(newSize);
34066                     this.fireEvent("resized", this, newSize);
34067                 break;                
34068             }
34069         }
34070     },
34071     
34072     getBox : function(){
34073         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34074     },
34075     
34076     getMargins : function(){
34077         return this.margins;
34078     },
34079     
34080     updateBox : function(box){
34081         this.box = box;
34082         var el = this.activePanel.getEl();
34083         el.dom.style.left = box.x + "px";
34084         el.dom.style.top = box.y + "px";
34085         this.activePanel.setSize(box.width, box.height);
34086     },
34087     
34088     /**
34089      * Returns the container element for this region.
34090      * @return {Roo.Element}
34091      */
34092     getEl : function(){
34093         return this.activePanel;
34094     },
34095     
34096     /**
34097      * Returns true if this region is currently visible.
34098      * @return {Boolean}
34099      */
34100     isVisible : function(){
34101         return this.activePanel ? true : false;
34102     },
34103     
34104     setActivePanel : function(panel){
34105         panel = this.getPanel(panel);
34106         if(this.activePanel && this.activePanel != panel){
34107             this.activePanel.setActiveState(false);
34108             this.activePanel.getEl().setLeftTop(-10000,-10000);
34109         }
34110         this.activePanel = panel;
34111         panel.setActiveState(true);
34112         if(this.box){
34113             panel.setSize(this.box.width, this.box.height);
34114         }
34115         this.fireEvent("panelactivated", this, panel);
34116         this.fireEvent("invalidated");
34117     },
34118     
34119     /**
34120      * Show the specified panel.
34121      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34122      * @return {Roo.ContentPanel} The shown panel or null
34123      */
34124     showPanel : function(panel){
34125         panel = this.getPanel(panel);
34126         if(panel){
34127             this.setActivePanel(panel);
34128         }
34129         return panel;
34130     },
34131     
34132     /**
34133      * Get the active panel for this region.
34134      * @return {Roo.ContentPanel} The active panel or null
34135      */
34136     getActivePanel : function(){
34137         return this.activePanel;
34138     },
34139     
34140     /**
34141      * Add the passed ContentPanel(s)
34142      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34143      * @return {Roo.ContentPanel} The panel added (if only one was added)
34144      */
34145     add : function(panel){
34146         if(arguments.length > 1){
34147             for(var i = 0, len = arguments.length; i < len; i++) {
34148                 this.add(arguments[i]);
34149             }
34150             return null;
34151         }
34152         if(this.hasPanel(panel)){
34153             this.showPanel(panel);
34154             return panel;
34155         }
34156         var el = panel.getEl();
34157         if(el.dom.parentNode != this.mgr.el.dom){
34158             this.mgr.el.dom.appendChild(el.dom);
34159         }
34160         if(panel.setRegion){
34161             panel.setRegion(this);
34162         }
34163         this.panels.add(panel);
34164         el.setStyle("position", "absolute");
34165         if(!panel.background){
34166             this.setActivePanel(panel);
34167             if(this.config.initialSize && this.panels.getCount()==1){
34168                 this.resizeTo(this.config.initialSize);
34169             }
34170         }
34171         this.fireEvent("paneladded", this, panel);
34172         return panel;
34173     },
34174     
34175     /**
34176      * Returns true if the panel is in this region.
34177      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34178      * @return {Boolean}
34179      */
34180     hasPanel : function(panel){
34181         if(typeof panel == "object"){ // must be panel obj
34182             panel = panel.getId();
34183         }
34184         return this.getPanel(panel) ? true : false;
34185     },
34186     
34187     /**
34188      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34189      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34190      * @param {Boolean} preservePanel Overrides the config preservePanel option
34191      * @return {Roo.ContentPanel} The panel that was removed
34192      */
34193     remove : function(panel, preservePanel){
34194         panel = this.getPanel(panel);
34195         if(!panel){
34196             return null;
34197         }
34198         var e = {};
34199         this.fireEvent("beforeremove", this, panel, e);
34200         if(e.cancel === true){
34201             return null;
34202         }
34203         var panelId = panel.getId();
34204         this.panels.removeKey(panelId);
34205         return panel;
34206     },
34207     
34208     /**
34209      * Returns the panel specified or null if it's not in this region.
34210      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34211      * @return {Roo.ContentPanel}
34212      */
34213     getPanel : function(id){
34214         if(typeof id == "object"){ // must be panel obj
34215             return id;
34216         }
34217         return this.panels.get(id);
34218     },
34219     
34220     /**
34221      * Returns this regions position (north/south/east/west/center).
34222      * @return {String} 
34223      */
34224     getPosition: function(){
34225         return this.position;    
34226     }
34227 });/*
34228  * Based on:
34229  * Ext JS Library 1.1.1
34230  * Copyright(c) 2006-2007, Ext JS, LLC.
34231  *
34232  * Originally Released Under LGPL - original licence link has changed is not relivant.
34233  *
34234  * Fork - LGPL
34235  * <script type="text/javascript">
34236  */
34237  
34238 /**
34239  * @class Roo.bootstrap.layout.Region
34240  * @extends Roo.bootstrap.layout.Basic
34241  * This class represents a region in a layout manager.
34242  
34243  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34244  * @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})
34245  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34246  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34247  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34248  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34249  * @cfg {String}    title           The title for the region (overrides panel titles)
34250  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34251  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34252  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34253  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34254  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34255  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34256  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34257  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34258  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34259  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34260
34261  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34262  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34263  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34264  * @cfg {Number}    width           For East/West panels
34265  * @cfg {Number}    height          For North/South panels
34266  * @cfg {Boolean}   split           To show the splitter
34267  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34268  * 
34269  * @cfg {string}   cls             Extra CSS classes to add to region
34270  * 
34271  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34272  * @cfg {string}   region  the region that it inhabits..
34273  *
34274
34275  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34276  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34277
34278  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34279  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34280  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34281  */
34282 Roo.bootstrap.layout.Region = function(config)
34283 {
34284     this.applyConfig(config);
34285
34286     var mgr = config.mgr;
34287     var pos = config.region;
34288     config.skipConfig = true;
34289     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34290     
34291     if (mgr.el) {
34292         this.onRender(mgr.el);   
34293     }
34294      
34295     this.visible = true;
34296     this.collapsed = false;
34297     this.unrendered_panels = [];
34298 };
34299
34300 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34301
34302     position: '', // set by wrapper (eg. north/south etc..)
34303     unrendered_panels : null,  // unrendered panels.
34304     createBody : function(){
34305         /** This region's body element 
34306         * @type Roo.Element */
34307         this.bodyEl = this.el.createChild({
34308                 tag: "div",
34309                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34310         });
34311     },
34312
34313     onRender: function(ctr, pos)
34314     {
34315         var dh = Roo.DomHelper;
34316         /** This region's container element 
34317         * @type Roo.Element */
34318         this.el = dh.append(ctr.dom, {
34319                 tag: "div",
34320                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34321             }, true);
34322         /** This region's title element 
34323         * @type Roo.Element */
34324     
34325         this.titleEl = dh.append(this.el.dom,
34326             {
34327                     tag: "div",
34328                     unselectable: "on",
34329                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34330                     children:[
34331                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34332                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34333                     ]}, true);
34334         
34335         this.titleEl.enableDisplayMode();
34336         /** This region's title text element 
34337         * @type HTMLElement */
34338         this.titleTextEl = this.titleEl.dom.firstChild;
34339         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34340         /*
34341         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34342         this.closeBtn.enableDisplayMode();
34343         this.closeBtn.on("click", this.closeClicked, this);
34344         this.closeBtn.hide();
34345     */
34346         this.createBody(this.config);
34347         if(this.config.hideWhenEmpty){
34348             this.hide();
34349             this.on("paneladded", this.validateVisibility, this);
34350             this.on("panelremoved", this.validateVisibility, this);
34351         }
34352         if(this.autoScroll){
34353             this.bodyEl.setStyle("overflow", "auto");
34354         }else{
34355             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34356         }
34357         //if(c.titlebar !== false){
34358             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34359                 this.titleEl.hide();
34360             }else{
34361                 this.titleEl.show();
34362                 if(this.config.title){
34363                     this.titleTextEl.innerHTML = this.config.title;
34364                 }
34365             }
34366         //}
34367         if(this.config.collapsed){
34368             this.collapse(true);
34369         }
34370         if(this.config.hidden){
34371             this.hide();
34372         }
34373         
34374         if (this.unrendered_panels && this.unrendered_panels.length) {
34375             for (var i =0;i< this.unrendered_panels.length; i++) {
34376                 this.add(this.unrendered_panels[i]);
34377             }
34378             this.unrendered_panels = null;
34379             
34380         }
34381         
34382     },
34383     
34384     applyConfig : function(c)
34385     {
34386         /*
34387          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34388             var dh = Roo.DomHelper;
34389             if(c.titlebar !== false){
34390                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34391                 this.collapseBtn.on("click", this.collapse, this);
34392                 this.collapseBtn.enableDisplayMode();
34393                 /*
34394                 if(c.showPin === true || this.showPin){
34395                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34396                     this.stickBtn.enableDisplayMode();
34397                     this.stickBtn.on("click", this.expand, this);
34398                     this.stickBtn.hide();
34399                 }
34400                 
34401             }
34402             */
34403             /** This region's collapsed element
34404             * @type Roo.Element */
34405             /*
34406              *
34407             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34408                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34409             ]}, true);
34410             
34411             if(c.floatable !== false){
34412                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34413                this.collapsedEl.on("click", this.collapseClick, this);
34414             }
34415
34416             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34417                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34418                    id: "message", unselectable: "on", style:{"float":"left"}});
34419                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34420              }
34421             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34422             this.expandBtn.on("click", this.expand, this);
34423             
34424         }
34425         
34426         if(this.collapseBtn){
34427             this.collapseBtn.setVisible(c.collapsible == true);
34428         }
34429         
34430         this.cmargins = c.cmargins || this.cmargins ||
34431                          (this.position == "west" || this.position == "east" ?
34432                              {top: 0, left: 2, right:2, bottom: 0} :
34433                              {top: 2, left: 0, right:0, bottom: 2});
34434         */
34435         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34436         
34437         
34438         this.bottomTabs = c.tabPosition != "top";
34439         
34440         this.autoScroll = c.autoScroll || false;
34441         
34442         
34443        
34444         
34445         this.duration = c.duration || .30;
34446         this.slideDuration = c.slideDuration || .45;
34447         this.config = c;
34448        
34449     },
34450     /**
34451      * Returns true if this region is currently visible.
34452      * @return {Boolean}
34453      */
34454     isVisible : function(){
34455         return this.visible;
34456     },
34457
34458     /**
34459      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34460      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34461      */
34462     //setCollapsedTitle : function(title){
34463     //    title = title || "&#160;";
34464      //   if(this.collapsedTitleTextEl){
34465       //      this.collapsedTitleTextEl.innerHTML = title;
34466        // }
34467     //},
34468
34469     getBox : function(){
34470         var b;
34471       //  if(!this.collapsed){
34472             b = this.el.getBox(false, true);
34473        // }else{
34474           //  b = this.collapsedEl.getBox(false, true);
34475         //}
34476         return b;
34477     },
34478
34479     getMargins : function(){
34480         return this.margins;
34481         //return this.collapsed ? this.cmargins : this.margins;
34482     },
34483 /*
34484     highlight : function(){
34485         this.el.addClass("x-layout-panel-dragover");
34486     },
34487
34488     unhighlight : function(){
34489         this.el.removeClass("x-layout-panel-dragover");
34490     },
34491 */
34492     updateBox : function(box)
34493     {
34494         if (!this.bodyEl) {
34495             return; // not rendered yet..
34496         }
34497         
34498         this.box = box;
34499         if(!this.collapsed){
34500             this.el.dom.style.left = box.x + "px";
34501             this.el.dom.style.top = box.y + "px";
34502             this.updateBody(box.width, box.height);
34503         }else{
34504             this.collapsedEl.dom.style.left = box.x + "px";
34505             this.collapsedEl.dom.style.top = box.y + "px";
34506             this.collapsedEl.setSize(box.width, box.height);
34507         }
34508         if(this.tabs){
34509             this.tabs.autoSizeTabs();
34510         }
34511     },
34512
34513     updateBody : function(w, h)
34514     {
34515         if(w !== null){
34516             this.el.setWidth(w);
34517             w -= this.el.getBorderWidth("rl");
34518             if(this.config.adjustments){
34519                 w += this.config.adjustments[0];
34520             }
34521         }
34522         if(h !== null && h > 0){
34523             this.el.setHeight(h);
34524             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34525             h -= this.el.getBorderWidth("tb");
34526             if(this.config.adjustments){
34527                 h += this.config.adjustments[1];
34528             }
34529             this.bodyEl.setHeight(h);
34530             if(this.tabs){
34531                 h = this.tabs.syncHeight(h);
34532             }
34533         }
34534         if(this.panelSize){
34535             w = w !== null ? w : this.panelSize.width;
34536             h = h !== null ? h : this.panelSize.height;
34537         }
34538         if(this.activePanel){
34539             var el = this.activePanel.getEl();
34540             w = w !== null ? w : el.getWidth();
34541             h = h !== null ? h : el.getHeight();
34542             this.panelSize = {width: w, height: h};
34543             this.activePanel.setSize(w, h);
34544         }
34545         if(Roo.isIE && this.tabs){
34546             this.tabs.el.repaint();
34547         }
34548     },
34549
34550     /**
34551      * Returns the container element for this region.
34552      * @return {Roo.Element}
34553      */
34554     getEl : function(){
34555         return this.el;
34556     },
34557
34558     /**
34559      * Hides this region.
34560      */
34561     hide : function(){
34562         //if(!this.collapsed){
34563             this.el.dom.style.left = "-2000px";
34564             this.el.hide();
34565         //}else{
34566          //   this.collapsedEl.dom.style.left = "-2000px";
34567          //   this.collapsedEl.hide();
34568        // }
34569         this.visible = false;
34570         this.fireEvent("visibilitychange", this, false);
34571     },
34572
34573     /**
34574      * Shows this region if it was previously hidden.
34575      */
34576     show : function(){
34577         //if(!this.collapsed){
34578             this.el.show();
34579         //}else{
34580         //    this.collapsedEl.show();
34581        // }
34582         this.visible = true;
34583         this.fireEvent("visibilitychange", this, true);
34584     },
34585 /*
34586     closeClicked : function(){
34587         if(this.activePanel){
34588             this.remove(this.activePanel);
34589         }
34590     },
34591
34592     collapseClick : function(e){
34593         if(this.isSlid){
34594            e.stopPropagation();
34595            this.slideIn();
34596         }else{
34597            e.stopPropagation();
34598            this.slideOut();
34599         }
34600     },
34601 */
34602     /**
34603      * Collapses this region.
34604      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34605      */
34606     /*
34607     collapse : function(skipAnim, skipCheck = false){
34608         if(this.collapsed) {
34609             return;
34610         }
34611         
34612         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34613             
34614             this.collapsed = true;
34615             if(this.split){
34616                 this.split.el.hide();
34617             }
34618             if(this.config.animate && skipAnim !== true){
34619                 this.fireEvent("invalidated", this);
34620                 this.animateCollapse();
34621             }else{
34622                 this.el.setLocation(-20000,-20000);
34623                 this.el.hide();
34624                 this.collapsedEl.show();
34625                 this.fireEvent("collapsed", this);
34626                 this.fireEvent("invalidated", this);
34627             }
34628         }
34629         
34630     },
34631 */
34632     animateCollapse : function(){
34633         // overridden
34634     },
34635
34636     /**
34637      * Expands this region if it was previously collapsed.
34638      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34639      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34640      */
34641     /*
34642     expand : function(e, skipAnim){
34643         if(e) {
34644             e.stopPropagation();
34645         }
34646         if(!this.collapsed || this.el.hasActiveFx()) {
34647             return;
34648         }
34649         if(this.isSlid){
34650             this.afterSlideIn();
34651             skipAnim = true;
34652         }
34653         this.collapsed = false;
34654         if(this.config.animate && skipAnim !== true){
34655             this.animateExpand();
34656         }else{
34657             this.el.show();
34658             if(this.split){
34659                 this.split.el.show();
34660             }
34661             this.collapsedEl.setLocation(-2000,-2000);
34662             this.collapsedEl.hide();
34663             this.fireEvent("invalidated", this);
34664             this.fireEvent("expanded", this);
34665         }
34666     },
34667 */
34668     animateExpand : function(){
34669         // overridden
34670     },
34671
34672     initTabs : function()
34673     {
34674         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34675         
34676         var ts = new Roo.bootstrap.panel.Tabs({
34677                 el: this.bodyEl.dom,
34678                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34679                 disableTooltips: this.config.disableTabTips,
34680                 toolbar : this.config.toolbar
34681             });
34682         
34683         if(this.config.hideTabs){
34684             ts.stripWrap.setDisplayed(false);
34685         }
34686         this.tabs = ts;
34687         ts.resizeTabs = this.config.resizeTabs === true;
34688         ts.minTabWidth = this.config.minTabWidth || 40;
34689         ts.maxTabWidth = this.config.maxTabWidth || 250;
34690         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34691         ts.monitorResize = false;
34692         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34693         ts.bodyEl.addClass('roo-layout-tabs-body');
34694         this.panels.each(this.initPanelAsTab, this);
34695     },
34696
34697     initPanelAsTab : function(panel){
34698         var ti = this.tabs.addTab(
34699             panel.getEl().id,
34700             panel.getTitle(),
34701             null,
34702             this.config.closeOnTab && panel.isClosable(),
34703             panel.tpl
34704         );
34705         if(panel.tabTip !== undefined){
34706             ti.setTooltip(panel.tabTip);
34707         }
34708         ti.on("activate", function(){
34709               this.setActivePanel(panel);
34710         }, this);
34711         
34712         if(this.config.closeOnTab){
34713             ti.on("beforeclose", function(t, e){
34714                 e.cancel = true;
34715                 this.remove(panel);
34716             }, this);
34717         }
34718         
34719         panel.tabItem = ti;
34720         
34721         return ti;
34722     },
34723
34724     updatePanelTitle : function(panel, title)
34725     {
34726         if(this.activePanel == panel){
34727             this.updateTitle(title);
34728         }
34729         if(this.tabs){
34730             var ti = this.tabs.getTab(panel.getEl().id);
34731             ti.setText(title);
34732             if(panel.tabTip !== undefined){
34733                 ti.setTooltip(panel.tabTip);
34734             }
34735         }
34736     },
34737
34738     updateTitle : function(title){
34739         if(this.titleTextEl && !this.config.title){
34740             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34741         }
34742     },
34743
34744     setActivePanel : function(panel)
34745     {
34746         panel = this.getPanel(panel);
34747         if(this.activePanel && this.activePanel != panel){
34748             this.activePanel.setActiveState(false);
34749         }
34750         this.activePanel = panel;
34751         panel.setActiveState(true);
34752         if(this.panelSize){
34753             panel.setSize(this.panelSize.width, this.panelSize.height);
34754         }
34755         if(this.closeBtn){
34756             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34757         }
34758         this.updateTitle(panel.getTitle());
34759         if(this.tabs){
34760             this.fireEvent("invalidated", this);
34761         }
34762         this.fireEvent("panelactivated", this, panel);
34763     },
34764
34765     /**
34766      * Shows the specified panel.
34767      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34768      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34769      */
34770     showPanel : function(panel)
34771     {
34772         panel = this.getPanel(panel);
34773         if(panel){
34774             if(this.tabs){
34775                 var tab = this.tabs.getTab(panel.getEl().id);
34776                 if(tab.isHidden()){
34777                     this.tabs.unhideTab(tab.id);
34778                 }
34779                 tab.activate();
34780             }else{
34781                 this.setActivePanel(panel);
34782             }
34783         }
34784         return panel;
34785     },
34786
34787     /**
34788      * Get the active panel for this region.
34789      * @return {Roo.ContentPanel} The active panel or null
34790      */
34791     getActivePanel : function(){
34792         return this.activePanel;
34793     },
34794
34795     validateVisibility : function(){
34796         if(this.panels.getCount() < 1){
34797             this.updateTitle("&#160;");
34798             this.closeBtn.hide();
34799             this.hide();
34800         }else{
34801             if(!this.isVisible()){
34802                 this.show();
34803             }
34804         }
34805     },
34806
34807     /**
34808      * Adds the passed ContentPanel(s) to this region.
34809      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34810      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34811      */
34812     add : function(panel)
34813     {
34814         if(arguments.length > 1){
34815             for(var i = 0, len = arguments.length; i < len; i++) {
34816                 this.add(arguments[i]);
34817             }
34818             return null;
34819         }
34820         
34821         // if we have not been rendered yet, then we can not really do much of this..
34822         if (!this.bodyEl) {
34823             this.unrendered_panels.push(panel);
34824             return panel;
34825         }
34826         
34827         
34828         
34829         
34830         if(this.hasPanel(panel)){
34831             this.showPanel(panel);
34832             return panel;
34833         }
34834         panel.setRegion(this);
34835         this.panels.add(panel);
34836        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34837             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34838             // and hide them... ???
34839             this.bodyEl.dom.appendChild(panel.getEl().dom);
34840             if(panel.background !== true){
34841                 this.setActivePanel(panel);
34842             }
34843             this.fireEvent("paneladded", this, panel);
34844             return panel;
34845         }
34846         */
34847         if(!this.tabs){
34848             this.initTabs();
34849         }else{
34850             this.initPanelAsTab(panel);
34851         }
34852         
34853         
34854         if(panel.background !== true){
34855             this.tabs.activate(panel.getEl().id);
34856         }
34857         this.fireEvent("paneladded", this, panel);
34858         return panel;
34859     },
34860
34861     /**
34862      * Hides the tab for the specified panel.
34863      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34864      */
34865     hidePanel : function(panel){
34866         if(this.tabs && (panel = this.getPanel(panel))){
34867             this.tabs.hideTab(panel.getEl().id);
34868         }
34869     },
34870
34871     /**
34872      * Unhides the tab for a previously hidden panel.
34873      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34874      */
34875     unhidePanel : function(panel){
34876         if(this.tabs && (panel = this.getPanel(panel))){
34877             this.tabs.unhideTab(panel.getEl().id);
34878         }
34879     },
34880
34881     clearPanels : function(){
34882         while(this.panels.getCount() > 0){
34883              this.remove(this.panels.first());
34884         }
34885     },
34886
34887     /**
34888      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34889      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34890      * @param {Boolean} preservePanel Overrides the config preservePanel option
34891      * @return {Roo.ContentPanel} The panel that was removed
34892      */
34893     remove : function(panel, preservePanel)
34894     {
34895         panel = this.getPanel(panel);
34896         if(!panel){
34897             return null;
34898         }
34899         var e = {};
34900         this.fireEvent("beforeremove", this, panel, e);
34901         if(e.cancel === true){
34902             return null;
34903         }
34904         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34905         var panelId = panel.getId();
34906         this.panels.removeKey(panelId);
34907         if(preservePanel){
34908             document.body.appendChild(panel.getEl().dom);
34909         }
34910         if(this.tabs){
34911             this.tabs.removeTab(panel.getEl().id);
34912         }else if (!preservePanel){
34913             this.bodyEl.dom.removeChild(panel.getEl().dom);
34914         }
34915         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34916             var p = this.panels.first();
34917             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34918             tempEl.appendChild(p.getEl().dom);
34919             this.bodyEl.update("");
34920             this.bodyEl.dom.appendChild(p.getEl().dom);
34921             tempEl = null;
34922             this.updateTitle(p.getTitle());
34923             this.tabs = null;
34924             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34925             this.setActivePanel(p);
34926         }
34927         panel.setRegion(null);
34928         if(this.activePanel == panel){
34929             this.activePanel = null;
34930         }
34931         if(this.config.autoDestroy !== false && preservePanel !== true){
34932             try{panel.destroy();}catch(e){}
34933         }
34934         this.fireEvent("panelremoved", this, panel);
34935         return panel;
34936     },
34937
34938     /**
34939      * Returns the TabPanel component used by this region
34940      * @return {Roo.TabPanel}
34941      */
34942     getTabs : function(){
34943         return this.tabs;
34944     },
34945
34946     createTool : function(parentEl, className){
34947         var btn = Roo.DomHelper.append(parentEl, {
34948             tag: "div",
34949             cls: "x-layout-tools-button",
34950             children: [ {
34951                 tag: "div",
34952                 cls: "roo-layout-tools-button-inner " + className,
34953                 html: "&#160;"
34954             }]
34955         }, true);
34956         btn.addClassOnOver("roo-layout-tools-button-over");
34957         return btn;
34958     }
34959 });/*
34960  * Based on:
34961  * Ext JS Library 1.1.1
34962  * Copyright(c) 2006-2007, Ext JS, LLC.
34963  *
34964  * Originally Released Under LGPL - original licence link has changed is not relivant.
34965  *
34966  * Fork - LGPL
34967  * <script type="text/javascript">
34968  */
34969  
34970
34971
34972 /**
34973  * @class Roo.SplitLayoutRegion
34974  * @extends Roo.LayoutRegion
34975  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34976  */
34977 Roo.bootstrap.layout.Split = function(config){
34978     this.cursor = config.cursor;
34979     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34980 };
34981
34982 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34983 {
34984     splitTip : "Drag to resize.",
34985     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34986     useSplitTips : false,
34987
34988     applyConfig : function(config){
34989         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34990     },
34991     
34992     onRender : function(ctr,pos) {
34993         
34994         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34995         if(!this.config.split){
34996             return;
34997         }
34998         if(!this.split){
34999             
35000             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35001                             tag: "div",
35002                             id: this.el.id + "-split",
35003                             cls: "roo-layout-split roo-layout-split-"+this.position,
35004                             html: "&#160;"
35005             });
35006             /** The SplitBar for this region 
35007             * @type Roo.SplitBar */
35008             // does not exist yet...
35009             Roo.log([this.position, this.orientation]);
35010             
35011             this.split = new Roo.bootstrap.SplitBar({
35012                 dragElement : splitEl,
35013                 resizingElement: this.el,
35014                 orientation : this.orientation
35015             });
35016             
35017             this.split.on("moved", this.onSplitMove, this);
35018             this.split.useShim = this.config.useShim === true;
35019             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35020             if(this.useSplitTips){
35021                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35022             }
35023             //if(config.collapsible){
35024             //    this.split.el.on("dblclick", this.collapse,  this);
35025             //}
35026         }
35027         if(typeof this.config.minSize != "undefined"){
35028             this.split.minSize = this.config.minSize;
35029         }
35030         if(typeof this.config.maxSize != "undefined"){
35031             this.split.maxSize = this.config.maxSize;
35032         }
35033         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35034             this.hideSplitter();
35035         }
35036         
35037     },
35038
35039     getHMaxSize : function(){
35040          var cmax = this.config.maxSize || 10000;
35041          var center = this.mgr.getRegion("center");
35042          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35043     },
35044
35045     getVMaxSize : function(){
35046          var cmax = this.config.maxSize || 10000;
35047          var center = this.mgr.getRegion("center");
35048          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35049     },
35050
35051     onSplitMove : function(split, newSize){
35052         this.fireEvent("resized", this, newSize);
35053     },
35054     
35055     /** 
35056      * Returns the {@link Roo.SplitBar} for this region.
35057      * @return {Roo.SplitBar}
35058      */
35059     getSplitBar : function(){
35060         return this.split;
35061     },
35062     
35063     hide : function(){
35064         this.hideSplitter();
35065         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35066     },
35067
35068     hideSplitter : function(){
35069         if(this.split){
35070             this.split.el.setLocation(-2000,-2000);
35071             this.split.el.hide();
35072         }
35073     },
35074
35075     show : function(){
35076         if(this.split){
35077             this.split.el.show();
35078         }
35079         Roo.bootstrap.layout.Split.superclass.show.call(this);
35080     },
35081     
35082     beforeSlide: function(){
35083         if(Roo.isGecko){// firefox overflow auto bug workaround
35084             this.bodyEl.clip();
35085             if(this.tabs) {
35086                 this.tabs.bodyEl.clip();
35087             }
35088             if(this.activePanel){
35089                 this.activePanel.getEl().clip();
35090                 
35091                 if(this.activePanel.beforeSlide){
35092                     this.activePanel.beforeSlide();
35093                 }
35094             }
35095         }
35096     },
35097     
35098     afterSlide : function(){
35099         if(Roo.isGecko){// firefox overflow auto bug workaround
35100             this.bodyEl.unclip();
35101             if(this.tabs) {
35102                 this.tabs.bodyEl.unclip();
35103             }
35104             if(this.activePanel){
35105                 this.activePanel.getEl().unclip();
35106                 if(this.activePanel.afterSlide){
35107                     this.activePanel.afterSlide();
35108                 }
35109             }
35110         }
35111     },
35112
35113     initAutoHide : function(){
35114         if(this.autoHide !== false){
35115             if(!this.autoHideHd){
35116                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35117                 this.autoHideHd = {
35118                     "mouseout": function(e){
35119                         if(!e.within(this.el, true)){
35120                             st.delay(500);
35121                         }
35122                     },
35123                     "mouseover" : function(e){
35124                         st.cancel();
35125                     },
35126                     scope : this
35127                 };
35128             }
35129             this.el.on(this.autoHideHd);
35130         }
35131     },
35132
35133     clearAutoHide : function(){
35134         if(this.autoHide !== false){
35135             this.el.un("mouseout", this.autoHideHd.mouseout);
35136             this.el.un("mouseover", this.autoHideHd.mouseover);
35137         }
35138     },
35139
35140     clearMonitor : function(){
35141         Roo.get(document).un("click", this.slideInIf, this);
35142     },
35143
35144     // these names are backwards but not changed for compat
35145     slideOut : function(){
35146         if(this.isSlid || this.el.hasActiveFx()){
35147             return;
35148         }
35149         this.isSlid = true;
35150         if(this.collapseBtn){
35151             this.collapseBtn.hide();
35152         }
35153         this.closeBtnState = this.closeBtn.getStyle('display');
35154         this.closeBtn.hide();
35155         if(this.stickBtn){
35156             this.stickBtn.show();
35157         }
35158         this.el.show();
35159         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35160         this.beforeSlide();
35161         this.el.setStyle("z-index", 10001);
35162         this.el.slideIn(this.getSlideAnchor(), {
35163             callback: function(){
35164                 this.afterSlide();
35165                 this.initAutoHide();
35166                 Roo.get(document).on("click", this.slideInIf, this);
35167                 this.fireEvent("slideshow", this);
35168             },
35169             scope: this,
35170             block: true
35171         });
35172     },
35173
35174     afterSlideIn : function(){
35175         this.clearAutoHide();
35176         this.isSlid = false;
35177         this.clearMonitor();
35178         this.el.setStyle("z-index", "");
35179         if(this.collapseBtn){
35180             this.collapseBtn.show();
35181         }
35182         this.closeBtn.setStyle('display', this.closeBtnState);
35183         if(this.stickBtn){
35184             this.stickBtn.hide();
35185         }
35186         this.fireEvent("slidehide", this);
35187     },
35188
35189     slideIn : function(cb){
35190         if(!this.isSlid || this.el.hasActiveFx()){
35191             Roo.callback(cb);
35192             return;
35193         }
35194         this.isSlid = false;
35195         this.beforeSlide();
35196         this.el.slideOut(this.getSlideAnchor(), {
35197             callback: function(){
35198                 this.el.setLeftTop(-10000, -10000);
35199                 this.afterSlide();
35200                 this.afterSlideIn();
35201                 Roo.callback(cb);
35202             },
35203             scope: this,
35204             block: true
35205         });
35206     },
35207     
35208     slideInIf : function(e){
35209         if(!e.within(this.el)){
35210             this.slideIn();
35211         }
35212     },
35213
35214     animateCollapse : function(){
35215         this.beforeSlide();
35216         this.el.setStyle("z-index", 20000);
35217         var anchor = this.getSlideAnchor();
35218         this.el.slideOut(anchor, {
35219             callback : function(){
35220                 this.el.setStyle("z-index", "");
35221                 this.collapsedEl.slideIn(anchor, {duration:.3});
35222                 this.afterSlide();
35223                 this.el.setLocation(-10000,-10000);
35224                 this.el.hide();
35225                 this.fireEvent("collapsed", this);
35226             },
35227             scope: this,
35228             block: true
35229         });
35230     },
35231
35232     animateExpand : function(){
35233         this.beforeSlide();
35234         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35235         this.el.setStyle("z-index", 20000);
35236         this.collapsedEl.hide({
35237             duration:.1
35238         });
35239         this.el.slideIn(this.getSlideAnchor(), {
35240             callback : function(){
35241                 this.el.setStyle("z-index", "");
35242                 this.afterSlide();
35243                 if(this.split){
35244                     this.split.el.show();
35245                 }
35246                 this.fireEvent("invalidated", this);
35247                 this.fireEvent("expanded", this);
35248             },
35249             scope: this,
35250             block: true
35251         });
35252     },
35253
35254     anchors : {
35255         "west" : "left",
35256         "east" : "right",
35257         "north" : "top",
35258         "south" : "bottom"
35259     },
35260
35261     sanchors : {
35262         "west" : "l",
35263         "east" : "r",
35264         "north" : "t",
35265         "south" : "b"
35266     },
35267
35268     canchors : {
35269         "west" : "tl-tr",
35270         "east" : "tr-tl",
35271         "north" : "tl-bl",
35272         "south" : "bl-tl"
35273     },
35274
35275     getAnchor : function(){
35276         return this.anchors[this.position];
35277     },
35278
35279     getCollapseAnchor : function(){
35280         return this.canchors[this.position];
35281     },
35282
35283     getSlideAnchor : function(){
35284         return this.sanchors[this.position];
35285     },
35286
35287     getAlignAdj : function(){
35288         var cm = this.cmargins;
35289         switch(this.position){
35290             case "west":
35291                 return [0, 0];
35292             break;
35293             case "east":
35294                 return [0, 0];
35295             break;
35296             case "north":
35297                 return [0, 0];
35298             break;
35299             case "south":
35300                 return [0, 0];
35301             break;
35302         }
35303     },
35304
35305     getExpandAdj : function(){
35306         var c = this.collapsedEl, cm = this.cmargins;
35307         switch(this.position){
35308             case "west":
35309                 return [-(cm.right+c.getWidth()+cm.left), 0];
35310             break;
35311             case "east":
35312                 return [cm.right+c.getWidth()+cm.left, 0];
35313             break;
35314             case "north":
35315                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35316             break;
35317             case "south":
35318                 return [0, cm.top+cm.bottom+c.getHeight()];
35319             break;
35320         }
35321     }
35322 });/*
35323  * Based on:
35324  * Ext JS Library 1.1.1
35325  * Copyright(c) 2006-2007, Ext JS, LLC.
35326  *
35327  * Originally Released Under LGPL - original licence link has changed is not relivant.
35328  *
35329  * Fork - LGPL
35330  * <script type="text/javascript">
35331  */
35332 /*
35333  * These classes are private internal classes
35334  */
35335 Roo.bootstrap.layout.Center = function(config){
35336     config.region = "center";
35337     Roo.bootstrap.layout.Region.call(this, config);
35338     this.visible = true;
35339     this.minWidth = config.minWidth || 20;
35340     this.minHeight = config.minHeight || 20;
35341 };
35342
35343 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35344     hide : function(){
35345         // center panel can't be hidden
35346     },
35347     
35348     show : function(){
35349         // center panel can't be hidden
35350     },
35351     
35352     getMinWidth: function(){
35353         return this.minWidth;
35354     },
35355     
35356     getMinHeight: function(){
35357         return this.minHeight;
35358     }
35359 });
35360
35361
35362
35363
35364  
35365
35366
35367
35368
35369
35370 Roo.bootstrap.layout.North = function(config)
35371 {
35372     config.region = 'north';
35373     config.cursor = 'n-resize';
35374     
35375     Roo.bootstrap.layout.Split.call(this, config);
35376     
35377     
35378     if(this.split){
35379         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35380         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35381         this.split.el.addClass("roo-layout-split-v");
35382     }
35383     var size = config.initialSize || config.height;
35384     if(typeof size != "undefined"){
35385         this.el.setHeight(size);
35386     }
35387 };
35388 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35389 {
35390     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35391     
35392     
35393     
35394     getBox : function(){
35395         if(this.collapsed){
35396             return this.collapsedEl.getBox();
35397         }
35398         var box = this.el.getBox();
35399         if(this.split){
35400             box.height += this.split.el.getHeight();
35401         }
35402         return box;
35403     },
35404     
35405     updateBox : function(box){
35406         if(this.split && !this.collapsed){
35407             box.height -= this.split.el.getHeight();
35408             this.split.el.setLeft(box.x);
35409             this.split.el.setTop(box.y+box.height);
35410             this.split.el.setWidth(box.width);
35411         }
35412         if(this.collapsed){
35413             this.updateBody(box.width, null);
35414         }
35415         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35416     }
35417 });
35418
35419
35420
35421
35422
35423 Roo.bootstrap.layout.South = function(config){
35424     config.region = 'south';
35425     config.cursor = 's-resize';
35426     Roo.bootstrap.layout.Split.call(this, config);
35427     if(this.split){
35428         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35429         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35430         this.split.el.addClass("roo-layout-split-v");
35431     }
35432     var size = config.initialSize || config.height;
35433     if(typeof size != "undefined"){
35434         this.el.setHeight(size);
35435     }
35436 };
35437
35438 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35439     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35440     getBox : function(){
35441         if(this.collapsed){
35442             return this.collapsedEl.getBox();
35443         }
35444         var box = this.el.getBox();
35445         if(this.split){
35446             var sh = this.split.el.getHeight();
35447             box.height += sh;
35448             box.y -= sh;
35449         }
35450         return box;
35451     },
35452     
35453     updateBox : function(box){
35454         if(this.split && !this.collapsed){
35455             var sh = this.split.el.getHeight();
35456             box.height -= sh;
35457             box.y += sh;
35458             this.split.el.setLeft(box.x);
35459             this.split.el.setTop(box.y-sh);
35460             this.split.el.setWidth(box.width);
35461         }
35462         if(this.collapsed){
35463             this.updateBody(box.width, null);
35464         }
35465         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35466     }
35467 });
35468
35469 Roo.bootstrap.layout.East = function(config){
35470     config.region = "east";
35471     config.cursor = "e-resize";
35472     Roo.bootstrap.layout.Split.call(this, config);
35473     if(this.split){
35474         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35475         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35476         this.split.el.addClass("roo-layout-split-h");
35477     }
35478     var size = config.initialSize || config.width;
35479     if(typeof size != "undefined"){
35480         this.el.setWidth(size);
35481     }
35482 };
35483 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35484     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35485     getBox : function(){
35486         if(this.collapsed){
35487             return this.collapsedEl.getBox();
35488         }
35489         var box = this.el.getBox();
35490         if(this.split){
35491             var sw = this.split.el.getWidth();
35492             box.width += sw;
35493             box.x -= sw;
35494         }
35495         return box;
35496     },
35497
35498     updateBox : function(box){
35499         if(this.split && !this.collapsed){
35500             var sw = this.split.el.getWidth();
35501             box.width -= sw;
35502             this.split.el.setLeft(box.x);
35503             this.split.el.setTop(box.y);
35504             this.split.el.setHeight(box.height);
35505             box.x += sw;
35506         }
35507         if(this.collapsed){
35508             this.updateBody(null, box.height);
35509         }
35510         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35511     }
35512 });
35513
35514 Roo.bootstrap.layout.West = function(config){
35515     config.region = "west";
35516     config.cursor = "w-resize";
35517     
35518     Roo.bootstrap.layout.Split.call(this, config);
35519     if(this.split){
35520         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35521         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35522         this.split.el.addClass("roo-layout-split-h");
35523     }
35524     
35525 };
35526 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35527     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35528     
35529     onRender: function(ctr, pos)
35530     {
35531         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35532         var size = this.config.initialSize || this.config.width;
35533         if(typeof size != "undefined"){
35534             this.el.setWidth(size);
35535         }
35536     },
35537     
35538     getBox : function(){
35539         if(this.collapsed){
35540             return this.collapsedEl.getBox();
35541         }
35542         var box = this.el.getBox();
35543         if(this.split){
35544             box.width += this.split.el.getWidth();
35545         }
35546         return box;
35547     },
35548     
35549     updateBox : function(box){
35550         if(this.split && !this.collapsed){
35551             var sw = this.split.el.getWidth();
35552             box.width -= sw;
35553             this.split.el.setLeft(box.x+box.width);
35554             this.split.el.setTop(box.y);
35555             this.split.el.setHeight(box.height);
35556         }
35557         if(this.collapsed){
35558             this.updateBody(null, box.height);
35559         }
35560         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35561     }
35562 });
35563 Roo.namespace("Roo.bootstrap.panel");/*
35564  * Based on:
35565  * Ext JS Library 1.1.1
35566  * Copyright(c) 2006-2007, Ext JS, LLC.
35567  *
35568  * Originally Released Under LGPL - original licence link has changed is not relivant.
35569  *
35570  * Fork - LGPL
35571  * <script type="text/javascript">
35572  */
35573 /**
35574  * @class Roo.ContentPanel
35575  * @extends Roo.util.Observable
35576  * A basic ContentPanel element.
35577  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35578  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35579  * @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
35580  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35581  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35582  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35583  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35584  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35585  * @cfg {String} title          The title for this panel
35586  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35587  * @cfg {String} url            Calls {@link #setUrl} with this value
35588  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35589  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35590  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35591  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35592  * @cfg {Boolean} badges render the badges
35593
35594  * @constructor
35595  * Create a new ContentPanel.
35596  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35597  * @param {String/Object} config A string to set only the title or a config object
35598  * @param {String} content (optional) Set the HTML content for this panel
35599  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35600  */
35601 Roo.bootstrap.panel.Content = function( config){
35602     
35603     this.tpl = config.tpl || false;
35604     
35605     var el = config.el;
35606     var content = config.content;
35607
35608     if(config.autoCreate){ // xtype is available if this is called from factory
35609         el = Roo.id();
35610     }
35611     this.el = Roo.get(el);
35612     if(!this.el && config && config.autoCreate){
35613         if(typeof config.autoCreate == "object"){
35614             if(!config.autoCreate.id){
35615                 config.autoCreate.id = config.id||el;
35616             }
35617             this.el = Roo.DomHelper.append(document.body,
35618                         config.autoCreate, true);
35619         }else{
35620             var elcfg =  {   tag: "div",
35621                             cls: "roo-layout-inactive-content",
35622                             id: config.id||el
35623                             };
35624             if (config.html) {
35625                 elcfg.html = config.html;
35626                 
35627             }
35628                         
35629             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35630         }
35631     } 
35632     this.closable = false;
35633     this.loaded = false;
35634     this.active = false;
35635    
35636       
35637     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35638         
35639         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35640         
35641         this.wrapEl = this.el; //this.el.wrap();
35642         var ti = [];
35643         if (config.toolbar.items) {
35644             ti = config.toolbar.items ;
35645             delete config.toolbar.items ;
35646         }
35647         
35648         var nitems = [];
35649         this.toolbar.render(this.wrapEl, 'before');
35650         for(var i =0;i < ti.length;i++) {
35651           //  Roo.log(['add child', items[i]]);
35652             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35653         }
35654         this.toolbar.items = nitems;
35655         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35656         delete config.toolbar;
35657         
35658     }
35659     /*
35660     // xtype created footer. - not sure if will work as we normally have to render first..
35661     if (this.footer && !this.footer.el && this.footer.xtype) {
35662         if (!this.wrapEl) {
35663             this.wrapEl = this.el.wrap();
35664         }
35665     
35666         this.footer.container = this.wrapEl.createChild();
35667          
35668         this.footer = Roo.factory(this.footer, Roo);
35669         
35670     }
35671     */
35672     
35673      if(typeof config == "string"){
35674         this.title = config;
35675     }else{
35676         Roo.apply(this, config);
35677     }
35678     
35679     if(this.resizeEl){
35680         this.resizeEl = Roo.get(this.resizeEl, true);
35681     }else{
35682         this.resizeEl = this.el;
35683     }
35684     // handle view.xtype
35685     
35686  
35687     
35688     
35689     this.addEvents({
35690         /**
35691          * @event activate
35692          * Fires when this panel is activated. 
35693          * @param {Roo.ContentPanel} this
35694          */
35695         "activate" : true,
35696         /**
35697          * @event deactivate
35698          * Fires when this panel is activated. 
35699          * @param {Roo.ContentPanel} this
35700          */
35701         "deactivate" : true,
35702
35703         /**
35704          * @event resize
35705          * Fires when this panel is resized if fitToFrame is true.
35706          * @param {Roo.ContentPanel} this
35707          * @param {Number} width The width after any component adjustments
35708          * @param {Number} height The height after any component adjustments
35709          */
35710         "resize" : true,
35711         
35712          /**
35713          * @event render
35714          * Fires when this tab is created
35715          * @param {Roo.ContentPanel} this
35716          */
35717         "render" : true
35718         
35719         
35720         
35721     });
35722     
35723
35724     
35725     
35726     if(this.autoScroll){
35727         this.resizeEl.setStyle("overflow", "auto");
35728     } else {
35729         // fix randome scrolling
35730         //this.el.on('scroll', function() {
35731         //    Roo.log('fix random scolling');
35732         //    this.scrollTo('top',0); 
35733         //});
35734     }
35735     content = content || this.content;
35736     if(content){
35737         this.setContent(content);
35738     }
35739     if(config && config.url){
35740         this.setUrl(this.url, this.params, this.loadOnce);
35741     }
35742     
35743     
35744     
35745     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35746     
35747     if (this.view && typeof(this.view.xtype) != 'undefined') {
35748         this.view.el = this.el.appendChild(document.createElement("div"));
35749         this.view = Roo.factory(this.view); 
35750         this.view.render  &&  this.view.render(false, '');  
35751     }
35752     
35753     
35754     this.fireEvent('render', this);
35755 };
35756
35757 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35758     
35759     tabTip : '',
35760     
35761     setRegion : function(region){
35762         this.region = region;
35763         this.setActiveClass(region && !this.background);
35764     },
35765     
35766     
35767     setActiveClass: function(state)
35768     {
35769         if(state){
35770            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35771            this.el.setStyle('position','relative');
35772         }else{
35773            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35774            this.el.setStyle('position', 'absolute');
35775         } 
35776     },
35777     
35778     /**
35779      * Returns the toolbar for this Panel if one was configured. 
35780      * @return {Roo.Toolbar} 
35781      */
35782     getToolbar : function(){
35783         return this.toolbar;
35784     },
35785     
35786     setActiveState : function(active)
35787     {
35788         this.active = active;
35789         this.setActiveClass(active);
35790         if(!active){
35791             this.fireEvent("deactivate", this);
35792         }else{
35793             this.fireEvent("activate", this);
35794         }
35795     },
35796     /**
35797      * Updates this panel's element
35798      * @param {String} content The new content
35799      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35800     */
35801     setContent : function(content, loadScripts){
35802         this.el.update(content, loadScripts);
35803     },
35804
35805     ignoreResize : function(w, h){
35806         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35807             return true;
35808         }else{
35809             this.lastSize = {width: w, height: h};
35810             return false;
35811         }
35812     },
35813     /**
35814      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35815      * @return {Roo.UpdateManager} The UpdateManager
35816      */
35817     getUpdateManager : function(){
35818         return this.el.getUpdateManager();
35819     },
35820      /**
35821      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35822      * @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:
35823 <pre><code>
35824 panel.load({
35825     url: "your-url.php",
35826     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35827     callback: yourFunction,
35828     scope: yourObject, //(optional scope)
35829     discardUrl: false,
35830     nocache: false,
35831     text: "Loading...",
35832     timeout: 30,
35833     scripts: false
35834 });
35835 </code></pre>
35836      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35837      * 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.
35838      * @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}
35839      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35840      * @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.
35841      * @return {Roo.ContentPanel} this
35842      */
35843     load : function(){
35844         var um = this.el.getUpdateManager();
35845         um.update.apply(um, arguments);
35846         return this;
35847     },
35848
35849
35850     /**
35851      * 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.
35852      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35853      * @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)
35854      * @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)
35855      * @return {Roo.UpdateManager} The UpdateManager
35856      */
35857     setUrl : function(url, params, loadOnce){
35858         if(this.refreshDelegate){
35859             this.removeListener("activate", this.refreshDelegate);
35860         }
35861         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35862         this.on("activate", this.refreshDelegate);
35863         return this.el.getUpdateManager();
35864     },
35865     
35866     _handleRefresh : function(url, params, loadOnce){
35867         if(!loadOnce || !this.loaded){
35868             var updater = this.el.getUpdateManager();
35869             updater.update(url, params, this._setLoaded.createDelegate(this));
35870         }
35871     },
35872     
35873     _setLoaded : function(){
35874         this.loaded = true;
35875     }, 
35876     
35877     /**
35878      * Returns this panel's id
35879      * @return {String} 
35880      */
35881     getId : function(){
35882         return this.el.id;
35883     },
35884     
35885     /** 
35886      * Returns this panel's element - used by regiosn to add.
35887      * @return {Roo.Element} 
35888      */
35889     getEl : function(){
35890         return this.wrapEl || this.el;
35891     },
35892     
35893    
35894     
35895     adjustForComponents : function(width, height)
35896     {
35897         //Roo.log('adjustForComponents ');
35898         if(this.resizeEl != this.el){
35899             width -= this.el.getFrameWidth('lr');
35900             height -= this.el.getFrameWidth('tb');
35901         }
35902         if(this.toolbar){
35903             var te = this.toolbar.getEl();
35904             te.setWidth(width);
35905             height -= te.getHeight();
35906         }
35907         if(this.footer){
35908             var te = this.footer.getEl();
35909             te.setWidth(width);
35910             height -= te.getHeight();
35911         }
35912         
35913         
35914         if(this.adjustments){
35915             width += this.adjustments[0];
35916             height += this.adjustments[1];
35917         }
35918         return {"width": width, "height": height};
35919     },
35920     
35921     setSize : function(width, height){
35922         if(this.fitToFrame && !this.ignoreResize(width, height)){
35923             if(this.fitContainer && this.resizeEl != this.el){
35924                 this.el.setSize(width, height);
35925             }
35926             var size = this.adjustForComponents(width, height);
35927             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35928             this.fireEvent('resize', this, size.width, size.height);
35929         }
35930     },
35931     
35932     /**
35933      * Returns this panel's title
35934      * @return {String} 
35935      */
35936     getTitle : function(){
35937         
35938         if (typeof(this.title) != 'object') {
35939             return this.title;
35940         }
35941         
35942         var t = '';
35943         for (var k in this.title) {
35944             if (!this.title.hasOwnProperty(k)) {
35945                 continue;
35946             }
35947             
35948             if (k.indexOf('-') >= 0) {
35949                 var s = k.split('-');
35950                 for (var i = 0; i<s.length; i++) {
35951                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
35952                 }
35953             } else {
35954                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
35955             }
35956         }
35957         return t;
35958     },
35959     
35960     /**
35961      * Set this panel's title
35962      * @param {String} title
35963      */
35964     setTitle : function(title){
35965         this.title = title;
35966         if(this.region){
35967             this.region.updatePanelTitle(this, title);
35968         }
35969     },
35970     
35971     /**
35972      * Returns true is this panel was configured to be closable
35973      * @return {Boolean} 
35974      */
35975     isClosable : function(){
35976         return this.closable;
35977     },
35978     
35979     beforeSlide : function(){
35980         this.el.clip();
35981         this.resizeEl.clip();
35982     },
35983     
35984     afterSlide : function(){
35985         this.el.unclip();
35986         this.resizeEl.unclip();
35987     },
35988     
35989     /**
35990      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35991      *   Will fail silently if the {@link #setUrl} method has not been called.
35992      *   This does not activate the panel, just updates its content.
35993      */
35994     refresh : function(){
35995         if(this.refreshDelegate){
35996            this.loaded = false;
35997            this.refreshDelegate();
35998         }
35999     },
36000     
36001     /**
36002      * Destroys this panel
36003      */
36004     destroy : function(){
36005         this.el.removeAllListeners();
36006         var tempEl = document.createElement("span");
36007         tempEl.appendChild(this.el.dom);
36008         tempEl.innerHTML = "";
36009         this.el.remove();
36010         this.el = null;
36011     },
36012     
36013     /**
36014      * form - if the content panel contains a form - this is a reference to it.
36015      * @type {Roo.form.Form}
36016      */
36017     form : false,
36018     /**
36019      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36020      *    This contains a reference to it.
36021      * @type {Roo.View}
36022      */
36023     view : false,
36024     
36025       /**
36026      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36027      * <pre><code>
36028
36029 layout.addxtype({
36030        xtype : 'Form',
36031        items: [ .... ]
36032    }
36033 );
36034
36035 </code></pre>
36036      * @param {Object} cfg Xtype definition of item to add.
36037      */
36038     
36039     
36040     getChildContainer: function () {
36041         return this.getEl();
36042     }
36043     
36044     
36045     /*
36046         var  ret = new Roo.factory(cfg);
36047         return ret;
36048         
36049         
36050         // add form..
36051         if (cfg.xtype.match(/^Form$/)) {
36052             
36053             var el;
36054             //if (this.footer) {
36055             //    el = this.footer.container.insertSibling(false, 'before');
36056             //} else {
36057                 el = this.el.createChild();
36058             //}
36059
36060             this.form = new  Roo.form.Form(cfg);
36061             
36062             
36063             if ( this.form.allItems.length) {
36064                 this.form.render(el.dom);
36065             }
36066             return this.form;
36067         }
36068         // should only have one of theses..
36069         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36070             // views.. should not be just added - used named prop 'view''
36071             
36072             cfg.el = this.el.appendChild(document.createElement("div"));
36073             // factory?
36074             
36075             var ret = new Roo.factory(cfg);
36076              
36077              ret.render && ret.render(false, ''); // render blank..
36078             this.view = ret;
36079             return ret;
36080         }
36081         return false;
36082     }
36083     \*/
36084 });
36085  
36086 /**
36087  * @class Roo.bootstrap.panel.Grid
36088  * @extends Roo.bootstrap.panel.Content
36089  * @constructor
36090  * Create a new GridPanel.
36091  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36092  * @param {Object} config A the config object
36093   
36094  */
36095
36096
36097
36098 Roo.bootstrap.panel.Grid = function(config)
36099 {
36100     
36101       
36102     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36103         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36104
36105     config.el = this.wrapper;
36106     //this.el = this.wrapper;
36107     
36108       if (config.container) {
36109         // ctor'ed from a Border/panel.grid
36110         
36111         
36112         this.wrapper.setStyle("overflow", "hidden");
36113         this.wrapper.addClass('roo-grid-container');
36114
36115     }
36116     
36117     
36118     if(config.toolbar){
36119         var tool_el = this.wrapper.createChild();    
36120         this.toolbar = Roo.factory(config.toolbar);
36121         var ti = [];
36122         if (config.toolbar.items) {
36123             ti = config.toolbar.items ;
36124             delete config.toolbar.items ;
36125         }
36126         
36127         var nitems = [];
36128         this.toolbar.render(tool_el);
36129         for(var i =0;i < ti.length;i++) {
36130           //  Roo.log(['add child', items[i]]);
36131             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36132         }
36133         this.toolbar.items = nitems;
36134         
36135         delete config.toolbar;
36136     }
36137     
36138     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36139     config.grid.scrollBody = true;;
36140     config.grid.monitorWindowResize = false; // turn off autosizing
36141     config.grid.autoHeight = false;
36142     config.grid.autoWidth = false;
36143     
36144     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36145     
36146     if (config.background) {
36147         // render grid on panel activation (if panel background)
36148         this.on('activate', function(gp) {
36149             if (!gp.grid.rendered) {
36150                 gp.grid.render(this.wrapper);
36151                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36152             }
36153         });
36154             
36155     } else {
36156         this.grid.render(this.wrapper);
36157         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36158
36159     }
36160     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36161     // ??? needed ??? config.el = this.wrapper;
36162     
36163     
36164     
36165   
36166     // xtype created footer. - not sure if will work as we normally have to render first..
36167     if (this.footer && !this.footer.el && this.footer.xtype) {
36168         
36169         var ctr = this.grid.getView().getFooterPanel(true);
36170         this.footer.dataSource = this.grid.dataSource;
36171         this.footer = Roo.factory(this.footer, Roo);
36172         this.footer.render(ctr);
36173         
36174     }
36175     
36176     
36177     
36178     
36179      
36180 };
36181
36182 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36183     getId : function(){
36184         return this.grid.id;
36185     },
36186     
36187     /**
36188      * Returns the grid for this panel
36189      * @return {Roo.bootstrap.Table} 
36190      */
36191     getGrid : function(){
36192         return this.grid;    
36193     },
36194     
36195     setSize : function(width, height){
36196         if(!this.ignoreResize(width, height)){
36197             var grid = this.grid;
36198             var size = this.adjustForComponents(width, height);
36199             var gridel = grid.getGridEl();
36200             gridel.setSize(size.width, size.height);
36201             /*
36202             var thd = grid.getGridEl().select('thead',true).first();
36203             var tbd = grid.getGridEl().select('tbody', true).first();
36204             if (tbd) {
36205                 tbd.setSize(width, height - thd.getHeight());
36206             }
36207             */
36208             grid.autoSize();
36209         }
36210     },
36211      
36212     
36213     
36214     beforeSlide : function(){
36215         this.grid.getView().scroller.clip();
36216     },
36217     
36218     afterSlide : function(){
36219         this.grid.getView().scroller.unclip();
36220     },
36221     
36222     destroy : function(){
36223         this.grid.destroy();
36224         delete this.grid;
36225         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36226     }
36227 });
36228
36229 /**
36230  * @class Roo.bootstrap.panel.Nest
36231  * @extends Roo.bootstrap.panel.Content
36232  * @constructor
36233  * Create a new Panel, that can contain a layout.Border.
36234  * 
36235  * 
36236  * @param {Roo.BorderLayout} layout The layout for this panel
36237  * @param {String/Object} config A string to set only the title or a config object
36238  */
36239 Roo.bootstrap.panel.Nest = function(config)
36240 {
36241     // construct with only one argument..
36242     /* FIXME - implement nicer consturctors
36243     if (layout.layout) {
36244         config = layout;
36245         layout = config.layout;
36246         delete config.layout;
36247     }
36248     if (layout.xtype && !layout.getEl) {
36249         // then layout needs constructing..
36250         layout = Roo.factory(layout, Roo);
36251     }
36252     */
36253     
36254     config.el =  config.layout.getEl();
36255     
36256     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36257     
36258     config.layout.monitorWindowResize = false; // turn off autosizing
36259     this.layout = config.layout;
36260     this.layout.getEl().addClass("roo-layout-nested-layout");
36261     
36262     
36263     
36264     
36265 };
36266
36267 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36268
36269     setSize : function(width, height){
36270         if(!this.ignoreResize(width, height)){
36271             var size = this.adjustForComponents(width, height);
36272             var el = this.layout.getEl();
36273             if (size.height < 1) {
36274                 el.setWidth(size.width);   
36275             } else {
36276                 el.setSize(size.width, size.height);
36277             }
36278             var touch = el.dom.offsetWidth;
36279             this.layout.layout();
36280             // ie requires a double layout on the first pass
36281             if(Roo.isIE && !this.initialized){
36282                 this.initialized = true;
36283                 this.layout.layout();
36284             }
36285         }
36286     },
36287     
36288     // activate all subpanels if not currently active..
36289     
36290     setActiveState : function(active){
36291         this.active = active;
36292         this.setActiveClass(active);
36293         
36294         if(!active){
36295             this.fireEvent("deactivate", this);
36296             return;
36297         }
36298         
36299         this.fireEvent("activate", this);
36300         // not sure if this should happen before or after..
36301         if (!this.layout) {
36302             return; // should not happen..
36303         }
36304         var reg = false;
36305         for (var r in this.layout.regions) {
36306             reg = this.layout.getRegion(r);
36307             if (reg.getActivePanel()) {
36308                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36309                 reg.setActivePanel(reg.getActivePanel());
36310                 continue;
36311             }
36312             if (!reg.panels.length) {
36313                 continue;
36314             }
36315             reg.showPanel(reg.getPanel(0));
36316         }
36317         
36318         
36319         
36320         
36321     },
36322     
36323     /**
36324      * Returns the nested BorderLayout for this panel
36325      * @return {Roo.BorderLayout} 
36326      */
36327     getLayout : function(){
36328         return this.layout;
36329     },
36330     
36331      /**
36332      * Adds a xtype elements to the layout of the nested panel
36333      * <pre><code>
36334
36335 panel.addxtype({
36336        xtype : 'ContentPanel',
36337        region: 'west',
36338        items: [ .... ]
36339    }
36340 );
36341
36342 panel.addxtype({
36343         xtype : 'NestedLayoutPanel',
36344         region: 'west',
36345         layout: {
36346            center: { },
36347            west: { }   
36348         },
36349         items : [ ... list of content panels or nested layout panels.. ]
36350    }
36351 );
36352 </code></pre>
36353      * @param {Object} cfg Xtype definition of item to add.
36354      */
36355     addxtype : function(cfg) {
36356         return this.layout.addxtype(cfg);
36357     
36358     }
36359 });        /*
36360  * Based on:
36361  * Ext JS Library 1.1.1
36362  * Copyright(c) 2006-2007, Ext JS, LLC.
36363  *
36364  * Originally Released Under LGPL - original licence link has changed is not relivant.
36365  *
36366  * Fork - LGPL
36367  * <script type="text/javascript">
36368  */
36369 /**
36370  * @class Roo.TabPanel
36371  * @extends Roo.util.Observable
36372  * A lightweight tab container.
36373  * <br><br>
36374  * Usage:
36375  * <pre><code>
36376 // basic tabs 1, built from existing content
36377 var tabs = new Roo.TabPanel("tabs1");
36378 tabs.addTab("script", "View Script");
36379 tabs.addTab("markup", "View Markup");
36380 tabs.activate("script");
36381
36382 // more advanced tabs, built from javascript
36383 var jtabs = new Roo.TabPanel("jtabs");
36384 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36385
36386 // set up the UpdateManager
36387 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36388 var updater = tab2.getUpdateManager();
36389 updater.setDefaultUrl("ajax1.htm");
36390 tab2.on('activate', updater.refresh, updater, true);
36391
36392 // Use setUrl for Ajax loading
36393 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36394 tab3.setUrl("ajax2.htm", null, true);
36395
36396 // Disabled tab
36397 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36398 tab4.disable();
36399
36400 jtabs.activate("jtabs-1");
36401  * </code></pre>
36402  * @constructor
36403  * Create a new TabPanel.
36404  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36405  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36406  */
36407 Roo.bootstrap.panel.Tabs = function(config){
36408     /**
36409     * The container element for this TabPanel.
36410     * @type Roo.Element
36411     */
36412     this.el = Roo.get(config.el);
36413     delete config.el;
36414     if(config){
36415         if(typeof config == "boolean"){
36416             this.tabPosition = config ? "bottom" : "top";
36417         }else{
36418             Roo.apply(this, config);
36419         }
36420     }
36421     
36422     if(this.tabPosition == "bottom"){
36423         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36424         this.el.addClass("roo-tabs-bottom");
36425     }
36426     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36427     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36428     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36429     if(Roo.isIE){
36430         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36431     }
36432     if(this.tabPosition != "bottom"){
36433         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36434          * @type Roo.Element
36435          */
36436         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36437         this.el.addClass("roo-tabs-top");
36438     }
36439     this.items = [];
36440
36441     this.bodyEl.setStyle("position", "relative");
36442
36443     this.active = null;
36444     this.activateDelegate = this.activate.createDelegate(this);
36445
36446     this.addEvents({
36447         /**
36448          * @event tabchange
36449          * Fires when the active tab changes
36450          * @param {Roo.TabPanel} this
36451          * @param {Roo.TabPanelItem} activePanel The new active tab
36452          */
36453         "tabchange": true,
36454         /**
36455          * @event beforetabchange
36456          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36457          * @param {Roo.TabPanel} this
36458          * @param {Object} e Set cancel to true on this object to cancel the tab change
36459          * @param {Roo.TabPanelItem} tab The tab being changed to
36460          */
36461         "beforetabchange" : true
36462     });
36463
36464     Roo.EventManager.onWindowResize(this.onResize, this);
36465     this.cpad = this.el.getPadding("lr");
36466     this.hiddenCount = 0;
36467
36468
36469     // toolbar on the tabbar support...
36470     if (this.toolbar) {
36471         alert("no toolbar support yet");
36472         this.toolbar  = false;
36473         /*
36474         var tcfg = this.toolbar;
36475         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36476         this.toolbar = new Roo.Toolbar(tcfg);
36477         if (Roo.isSafari) {
36478             var tbl = tcfg.container.child('table', true);
36479             tbl.setAttribute('width', '100%');
36480         }
36481         */
36482         
36483     }
36484    
36485
36486
36487     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36488 };
36489
36490 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36491     /*
36492      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36493      */
36494     tabPosition : "top",
36495     /*
36496      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36497      */
36498     currentTabWidth : 0,
36499     /*
36500      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36501      */
36502     minTabWidth : 40,
36503     /*
36504      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36505      */
36506     maxTabWidth : 250,
36507     /*
36508      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36509      */
36510     preferredTabWidth : 175,
36511     /*
36512      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36513      */
36514     resizeTabs : false,
36515     /*
36516      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36517      */
36518     monitorResize : true,
36519     /*
36520      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36521      */
36522     toolbar : false,
36523
36524     /**
36525      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36526      * @param {String} id The id of the div to use <b>or create</b>
36527      * @param {String} text The text for the tab
36528      * @param {String} content (optional) Content to put in the TabPanelItem body
36529      * @param {Boolean} closable (optional) True to create a close icon on the tab
36530      * @return {Roo.TabPanelItem} The created TabPanelItem
36531      */
36532     addTab : function(id, text, content, closable, tpl)
36533     {
36534         var item = new Roo.bootstrap.panel.TabItem({
36535             panel: this,
36536             id : id,
36537             text : text,
36538             closable : closable,
36539             tpl : tpl
36540         });
36541         this.addTabItem(item);
36542         if(content){
36543             item.setContent(content);
36544         }
36545         return item;
36546     },
36547
36548     /**
36549      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36550      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36551      * @return {Roo.TabPanelItem}
36552      */
36553     getTab : function(id){
36554         return this.items[id];
36555     },
36556
36557     /**
36558      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36559      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36560      */
36561     hideTab : function(id){
36562         var t = this.items[id];
36563         if(!t.isHidden()){
36564            t.setHidden(true);
36565            this.hiddenCount++;
36566            this.autoSizeTabs();
36567         }
36568     },
36569
36570     /**
36571      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36572      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36573      */
36574     unhideTab : function(id){
36575         var t = this.items[id];
36576         if(t.isHidden()){
36577            t.setHidden(false);
36578            this.hiddenCount--;
36579            this.autoSizeTabs();
36580         }
36581     },
36582
36583     /**
36584      * Adds an existing {@link Roo.TabPanelItem}.
36585      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36586      */
36587     addTabItem : function(item){
36588         this.items[item.id] = item;
36589         this.items.push(item);
36590       //  if(this.resizeTabs){
36591     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36592   //         this.autoSizeTabs();
36593 //        }else{
36594 //            item.autoSize();
36595        // }
36596     },
36597
36598     /**
36599      * Removes a {@link Roo.TabPanelItem}.
36600      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36601      */
36602     removeTab : function(id){
36603         var items = this.items;
36604         var tab = items[id];
36605         if(!tab) { return; }
36606         var index = items.indexOf(tab);
36607         if(this.active == tab && items.length > 1){
36608             var newTab = this.getNextAvailable(index);
36609             if(newTab) {
36610                 newTab.activate();
36611             }
36612         }
36613         this.stripEl.dom.removeChild(tab.pnode.dom);
36614         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36615             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36616         }
36617         items.splice(index, 1);
36618         delete this.items[tab.id];
36619         tab.fireEvent("close", tab);
36620         tab.purgeListeners();
36621         this.autoSizeTabs();
36622     },
36623
36624     getNextAvailable : function(start){
36625         var items = this.items;
36626         var index = start;
36627         // look for a next tab that will slide over to
36628         // replace the one being removed
36629         while(index < items.length){
36630             var item = items[++index];
36631             if(item && !item.isHidden()){
36632                 return item;
36633             }
36634         }
36635         // if one isn't found select the previous tab (on the left)
36636         index = start;
36637         while(index >= 0){
36638             var item = items[--index];
36639             if(item && !item.isHidden()){
36640                 return item;
36641             }
36642         }
36643         return null;
36644     },
36645
36646     /**
36647      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36648      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36649      */
36650     disableTab : function(id){
36651         var tab = this.items[id];
36652         if(tab && this.active != tab){
36653             tab.disable();
36654         }
36655     },
36656
36657     /**
36658      * Enables a {@link Roo.TabPanelItem} that is disabled.
36659      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36660      */
36661     enableTab : function(id){
36662         var tab = this.items[id];
36663         tab.enable();
36664     },
36665
36666     /**
36667      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36668      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36669      * @return {Roo.TabPanelItem} The TabPanelItem.
36670      */
36671     activate : function(id){
36672         var tab = this.items[id];
36673         if(!tab){
36674             return null;
36675         }
36676         if(tab == this.active || tab.disabled){
36677             return tab;
36678         }
36679         var e = {};
36680         this.fireEvent("beforetabchange", this, e, tab);
36681         if(e.cancel !== true && !tab.disabled){
36682             if(this.active){
36683                 this.active.hide();
36684             }
36685             this.active = this.items[id];
36686             this.active.show();
36687             this.fireEvent("tabchange", this, this.active);
36688         }
36689         return tab;
36690     },
36691
36692     /**
36693      * Gets the active {@link Roo.TabPanelItem}.
36694      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36695      */
36696     getActiveTab : function(){
36697         return this.active;
36698     },
36699
36700     /**
36701      * Updates the tab body element to fit the height of the container element
36702      * for overflow scrolling
36703      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36704      */
36705     syncHeight : function(targetHeight){
36706         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36707         var bm = this.bodyEl.getMargins();
36708         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36709         this.bodyEl.setHeight(newHeight);
36710         return newHeight;
36711     },
36712
36713     onResize : function(){
36714         if(this.monitorResize){
36715             this.autoSizeTabs();
36716         }
36717     },
36718
36719     /**
36720      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36721      */
36722     beginUpdate : function(){
36723         this.updating = true;
36724     },
36725
36726     /**
36727      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36728      */
36729     endUpdate : function(){
36730         this.updating = false;
36731         this.autoSizeTabs();
36732     },
36733
36734     /**
36735      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36736      */
36737     autoSizeTabs : function(){
36738         var count = this.items.length;
36739         var vcount = count - this.hiddenCount;
36740         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36741             return;
36742         }
36743         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36744         var availWidth = Math.floor(w / vcount);
36745         var b = this.stripBody;
36746         if(b.getWidth() > w){
36747             var tabs = this.items;
36748             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36749             if(availWidth < this.minTabWidth){
36750                 /*if(!this.sleft){    // incomplete scrolling code
36751                     this.createScrollButtons();
36752                 }
36753                 this.showScroll();
36754                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36755             }
36756         }else{
36757             if(this.currentTabWidth < this.preferredTabWidth){
36758                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36759             }
36760         }
36761     },
36762
36763     /**
36764      * Returns the number of tabs in this TabPanel.
36765      * @return {Number}
36766      */
36767      getCount : function(){
36768          return this.items.length;
36769      },
36770
36771     /**
36772      * Resizes all the tabs to the passed width
36773      * @param {Number} The new width
36774      */
36775     setTabWidth : function(width){
36776         this.currentTabWidth = width;
36777         for(var i = 0, len = this.items.length; i < len; i++) {
36778                 if(!this.items[i].isHidden()) {
36779                 this.items[i].setWidth(width);
36780             }
36781         }
36782     },
36783
36784     /**
36785      * Destroys this TabPanel
36786      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36787      */
36788     destroy : function(removeEl){
36789         Roo.EventManager.removeResizeListener(this.onResize, this);
36790         for(var i = 0, len = this.items.length; i < len; i++){
36791             this.items[i].purgeListeners();
36792         }
36793         if(removeEl === true){
36794             this.el.update("");
36795             this.el.remove();
36796         }
36797     },
36798     
36799     createStrip : function(container)
36800     {
36801         var strip = document.createElement("nav");
36802         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36803         container.appendChild(strip);
36804         return strip;
36805     },
36806     
36807     createStripList : function(strip)
36808     {
36809         // div wrapper for retard IE
36810         // returns the "tr" element.
36811         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36812         //'<div class="x-tabs-strip-wrap">'+
36813           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36814           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36815         return strip.firstChild; //.firstChild.firstChild.firstChild;
36816     },
36817     createBody : function(container)
36818     {
36819         var body = document.createElement("div");
36820         Roo.id(body, "tab-body");
36821         //Roo.fly(body).addClass("x-tabs-body");
36822         Roo.fly(body).addClass("tab-content");
36823         container.appendChild(body);
36824         return body;
36825     },
36826     createItemBody :function(bodyEl, id){
36827         var body = Roo.getDom(id);
36828         if(!body){
36829             body = document.createElement("div");
36830             body.id = id;
36831         }
36832         //Roo.fly(body).addClass("x-tabs-item-body");
36833         Roo.fly(body).addClass("tab-pane");
36834          bodyEl.insertBefore(body, bodyEl.firstChild);
36835         return body;
36836     },
36837     /** @private */
36838     createStripElements :  function(stripEl, text, closable, tpl)
36839     {
36840         var td = document.createElement("li"); // was td..
36841         
36842         
36843         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36844         
36845         
36846         stripEl.appendChild(td);
36847         /*if(closable){
36848             td.className = "x-tabs-closable";
36849             if(!this.closeTpl){
36850                 this.closeTpl = new Roo.Template(
36851                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36852                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36853                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36854                 );
36855             }
36856             var el = this.closeTpl.overwrite(td, {"text": text});
36857             var close = el.getElementsByTagName("div")[0];
36858             var inner = el.getElementsByTagName("em")[0];
36859             return {"el": el, "close": close, "inner": inner};
36860         } else {
36861         */
36862         // not sure what this is..
36863 //            if(!this.tabTpl){
36864                 //this.tabTpl = new Roo.Template(
36865                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36866                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36867                 //);
36868 //                this.tabTpl = new Roo.Template(
36869 //                   '<a href="#">' +
36870 //                   '<span unselectable="on"' +
36871 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36872 //                            ' >{text}</span></a>'
36873 //                );
36874 //                
36875 //            }
36876
36877
36878             var template = tpl || this.tabTpl || false;
36879             
36880             if(!template){
36881                 
36882                 template = new Roo.Template(
36883                    '<a href="#">' +
36884                    '<span unselectable="on"' +
36885                             (this.disableTooltips ? '' : ' title="{text}"') +
36886                             ' >{text}</span></a>'
36887                 );
36888             }
36889             
36890             switch (typeof(template)) {
36891                 case 'object' :
36892                     break;
36893                 case 'string' :
36894                     template = new Roo.Template(template);
36895                     break;
36896                 default :
36897                     break;
36898             }
36899             
36900             var el = template.overwrite(td, {"text": text});
36901             
36902             var inner = el.getElementsByTagName("span")[0];
36903             
36904             return {"el": el, "inner": inner};
36905             
36906     }
36907         
36908     
36909 });
36910
36911 /**
36912  * @class Roo.TabPanelItem
36913  * @extends Roo.util.Observable
36914  * Represents an individual item (tab plus body) in a TabPanel.
36915  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36916  * @param {String} id The id of this TabPanelItem
36917  * @param {String} text The text for the tab of this TabPanelItem
36918  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36919  */
36920 Roo.bootstrap.panel.TabItem = function(config){
36921     /**
36922      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36923      * @type Roo.TabPanel
36924      */
36925     this.tabPanel = config.panel;
36926     /**
36927      * The id for this TabPanelItem
36928      * @type String
36929      */
36930     this.id = config.id;
36931     /** @private */
36932     this.disabled = false;
36933     /** @private */
36934     this.text = config.text;
36935     /** @private */
36936     this.loaded = false;
36937     this.closable = config.closable;
36938
36939     /**
36940      * The body element for this TabPanelItem.
36941      * @type Roo.Element
36942      */
36943     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36944     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36945     this.bodyEl.setStyle("display", "block");
36946     this.bodyEl.setStyle("zoom", "1");
36947     //this.hideAction();
36948
36949     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36950     /** @private */
36951     this.el = Roo.get(els.el);
36952     this.inner = Roo.get(els.inner, true);
36953     this.textEl = Roo.get(this.el.dom.firstChild, true);
36954     this.pnode = Roo.get(els.el.parentNode, true);
36955     this.el.on("mousedown", this.onTabMouseDown, this);
36956     this.el.on("click", this.onTabClick, this);
36957     /** @private */
36958     if(config.closable){
36959         var c = Roo.get(els.close, true);
36960         c.dom.title = this.closeText;
36961         c.addClassOnOver("close-over");
36962         c.on("click", this.closeClick, this);
36963      }
36964
36965     this.addEvents({
36966          /**
36967          * @event activate
36968          * Fires when this tab becomes the active tab.
36969          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36970          * @param {Roo.TabPanelItem} this
36971          */
36972         "activate": true,
36973         /**
36974          * @event beforeclose
36975          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36976          * @param {Roo.TabPanelItem} this
36977          * @param {Object} e Set cancel to true on this object to cancel the close.
36978          */
36979         "beforeclose": true,
36980         /**
36981          * @event close
36982          * Fires when this tab is closed.
36983          * @param {Roo.TabPanelItem} this
36984          */
36985          "close": true,
36986         /**
36987          * @event deactivate
36988          * Fires when this tab is no longer the active tab.
36989          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36990          * @param {Roo.TabPanelItem} this
36991          */
36992          "deactivate" : true
36993     });
36994     this.hidden = false;
36995
36996     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36997 };
36998
36999 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37000            {
37001     purgeListeners : function(){
37002        Roo.util.Observable.prototype.purgeListeners.call(this);
37003        this.el.removeAllListeners();
37004     },
37005     /**
37006      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37007      */
37008     show : function(){
37009         this.pnode.addClass("active");
37010         this.showAction();
37011         if(Roo.isOpera){
37012             this.tabPanel.stripWrap.repaint();
37013         }
37014         this.fireEvent("activate", this.tabPanel, this);
37015     },
37016
37017     /**
37018      * Returns true if this tab is the active tab.
37019      * @return {Boolean}
37020      */
37021     isActive : function(){
37022         return this.tabPanel.getActiveTab() == this;
37023     },
37024
37025     /**
37026      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37027      */
37028     hide : function(){
37029         this.pnode.removeClass("active");
37030         this.hideAction();
37031         this.fireEvent("deactivate", this.tabPanel, this);
37032     },
37033
37034     hideAction : function(){
37035         this.bodyEl.hide();
37036         this.bodyEl.setStyle("position", "absolute");
37037         this.bodyEl.setLeft("-20000px");
37038         this.bodyEl.setTop("-20000px");
37039     },
37040
37041     showAction : function(){
37042         this.bodyEl.setStyle("position", "relative");
37043         this.bodyEl.setTop("");
37044         this.bodyEl.setLeft("");
37045         this.bodyEl.show();
37046     },
37047
37048     /**
37049      * Set the tooltip for the tab.
37050      * @param {String} tooltip The tab's tooltip
37051      */
37052     setTooltip : function(text){
37053         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37054             this.textEl.dom.qtip = text;
37055             this.textEl.dom.removeAttribute('title');
37056         }else{
37057             this.textEl.dom.title = text;
37058         }
37059     },
37060
37061     onTabClick : function(e){
37062         e.preventDefault();
37063         this.tabPanel.activate(this.id);
37064     },
37065
37066     onTabMouseDown : function(e){
37067         e.preventDefault();
37068         this.tabPanel.activate(this.id);
37069     },
37070 /*
37071     getWidth : function(){
37072         return this.inner.getWidth();
37073     },
37074
37075     setWidth : function(width){
37076         var iwidth = width - this.pnode.getPadding("lr");
37077         this.inner.setWidth(iwidth);
37078         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37079         this.pnode.setWidth(width);
37080     },
37081 */
37082     /**
37083      * Show or hide the tab
37084      * @param {Boolean} hidden True to hide or false to show.
37085      */
37086     setHidden : function(hidden){
37087         this.hidden = hidden;
37088         this.pnode.setStyle("display", hidden ? "none" : "");
37089     },
37090
37091     /**
37092      * Returns true if this tab is "hidden"
37093      * @return {Boolean}
37094      */
37095     isHidden : function(){
37096         return this.hidden;
37097     },
37098
37099     /**
37100      * Returns the text for this tab
37101      * @return {String}
37102      */
37103     getText : function(){
37104         return this.text;
37105     },
37106     /*
37107     autoSize : function(){
37108         //this.el.beginMeasure();
37109         this.textEl.setWidth(1);
37110         /*
37111          *  #2804 [new] Tabs in Roojs
37112          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37113          */
37114         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37115         //this.el.endMeasure();
37116     //},
37117
37118     /**
37119      * Sets the text for the tab (Note: this also sets the tooltip text)
37120      * @param {String} text The tab's text and tooltip
37121      */
37122     setText : function(text){
37123         this.text = text;
37124         this.textEl.update(text);
37125         this.setTooltip(text);
37126         //if(!this.tabPanel.resizeTabs){
37127         //    this.autoSize();
37128         //}
37129     },
37130     /**
37131      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37132      */
37133     activate : function(){
37134         this.tabPanel.activate(this.id);
37135     },
37136
37137     /**
37138      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37139      */
37140     disable : function(){
37141         if(this.tabPanel.active != this){
37142             this.disabled = true;
37143             this.pnode.addClass("disabled");
37144         }
37145     },
37146
37147     /**
37148      * Enables this TabPanelItem if it was previously disabled.
37149      */
37150     enable : function(){
37151         this.disabled = false;
37152         this.pnode.removeClass("disabled");
37153     },
37154
37155     /**
37156      * Sets the content for this TabPanelItem.
37157      * @param {String} content The content
37158      * @param {Boolean} loadScripts true to look for and load scripts
37159      */
37160     setContent : function(content, loadScripts){
37161         this.bodyEl.update(content, loadScripts);
37162     },
37163
37164     /**
37165      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37166      * @return {Roo.UpdateManager} The UpdateManager
37167      */
37168     getUpdateManager : function(){
37169         return this.bodyEl.getUpdateManager();
37170     },
37171
37172     /**
37173      * Set a URL to be used to load the content for this TabPanelItem.
37174      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37175      * @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)
37176      * @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)
37177      * @return {Roo.UpdateManager} The UpdateManager
37178      */
37179     setUrl : function(url, params, loadOnce){
37180         if(this.refreshDelegate){
37181             this.un('activate', this.refreshDelegate);
37182         }
37183         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37184         this.on("activate", this.refreshDelegate);
37185         return this.bodyEl.getUpdateManager();
37186     },
37187
37188     /** @private */
37189     _handleRefresh : function(url, params, loadOnce){
37190         if(!loadOnce || !this.loaded){
37191             var updater = this.bodyEl.getUpdateManager();
37192             updater.update(url, params, this._setLoaded.createDelegate(this));
37193         }
37194     },
37195
37196     /**
37197      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37198      *   Will fail silently if the setUrl method has not been called.
37199      *   This does not activate the panel, just updates its content.
37200      */
37201     refresh : function(){
37202         if(this.refreshDelegate){
37203            this.loaded = false;
37204            this.refreshDelegate();
37205         }
37206     },
37207
37208     /** @private */
37209     _setLoaded : function(){
37210         this.loaded = true;
37211     },
37212
37213     /** @private */
37214     closeClick : function(e){
37215         var o = {};
37216         e.stopEvent();
37217         this.fireEvent("beforeclose", this, o);
37218         if(o.cancel !== true){
37219             this.tabPanel.removeTab(this.id);
37220         }
37221     },
37222     /**
37223      * The text displayed in the tooltip for the close icon.
37224      * @type String
37225      */
37226     closeText : "Close this tab"
37227 });